diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/afs/Kconfig | 8 | ||||
-rw-r--r-- | fs/afs/Makefile | 3 | ||||
-rw-r--r-- | fs/afs/cache.c | 503 | ||||
-rw-r--r-- | fs/afs/cache.h | 15 | ||||
-rw-r--r-- | fs/afs/cell.c | 16 | ||||
-rw-r--r-- | fs/afs/file.c | 220 | ||||
-rw-r--r-- | fs/afs/inode.c | 31 | ||||
-rw-r--r-- | fs/afs/internal.h | 53 | ||||
-rw-r--r-- | fs/afs/main.c | 27 | ||||
-rw-r--r-- | fs/afs/mntpt.c | 4 | ||||
-rw-r--r-- | fs/afs/vlocation.c | 25 | ||||
-rw-r--r-- | fs/afs/volume.c | 14 | ||||
-rw-r--r-- | fs/afs/write.c | 21 |
13 files changed, 582 insertions, 358 deletions
diff --git a/fs/afs/Kconfig b/fs/afs/Kconfig index e7b522fe15e1..5c4e61d3c772 100644 --- a/fs/afs/Kconfig +++ b/fs/afs/Kconfig | |||
@@ -19,3 +19,11 @@ config AFS_DEBUG | |||
19 | See <file:Documentation/filesystems/afs.txt> for more information. | 19 | See <file:Documentation/filesystems/afs.txt> for more information. |
20 | 20 | ||
21 | If unsure, say N. | 21 | If unsure, say N. |
22 | |||
23 | config AFS_FSCACHE | ||
24 | bool "Provide AFS client caching support (EXPERIMENTAL)" | ||
25 | depends on EXPERIMENTAL | ||
26 | depends on AFS_FS=m && FSCACHE || AFS_FS=y && FSCACHE=y | ||
27 | help | ||
28 | Say Y here if you want AFS data to be cached locally on disk through | ||
29 | the generic filesystem cache manager | ||
diff --git a/fs/afs/Makefile b/fs/afs/Makefile index a66671082cfb..4f64b95d57bd 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile | |||
@@ -2,7 +2,10 @@ | |||
2 | # Makefile for Red Hat Linux AFS client. | 2 | # Makefile for Red Hat Linux AFS client. |
3 | # | 3 | # |
4 | 4 | ||
5 | afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o | ||
6 | |||
5 | kafs-objs := \ | 7 | kafs-objs := \ |
8 | $(afs-cache-y) \ | ||
6 | callback.o \ | 9 | callback.o \ |
7 | cell.o \ | 10 | cell.o \ |
8 | cmservice.o \ | 11 | cmservice.o \ |
diff --git a/fs/afs/cache.c b/fs/afs/cache.c index de0d7de69edc..e2b1d3f16519 100644 --- a/fs/afs/cache.c +++ b/fs/afs/cache.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* AFS caching stuff | 1 | /* AFS caching stuff |
2 | * | 2 | * |
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. |
4 | * Written by David Howells (dhowells@redhat.com) | 4 | * Written by David Howells (dhowells@redhat.com) |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
@@ -9,248 +9,395 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #ifdef AFS_CACHING_SUPPORT | 12 | #include <linux/slab.h> |
13 | static cachefs_match_val_t afs_cell_cache_match(void *target, | 13 | #include <linux/sched.h> |
14 | const void *entry); | 14 | #include "internal.h" |
15 | static void afs_cell_cache_update(void *source, void *entry); | 15 | |
16 | 16 | static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data, | |
17 | struct cachefs_index_def afs_cache_cell_index_def = { | 17 | void *buffer, uint16_t buflen); |
18 | .name = "cell_ix", | 18 | static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data, |
19 | .data_size = sizeof(struct afs_cache_cell), | 19 | void *buffer, uint16_t buflen); |
20 | .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, | 20 | static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data, |
21 | .match = afs_cell_cache_match, | 21 | const void *buffer, |
22 | .update = afs_cell_cache_update, | 22 | uint16_t buflen); |
23 | |||
24 | static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data, | ||
25 | void *buffer, uint16_t buflen); | ||
26 | static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data, | ||
27 | void *buffer, uint16_t buflen); | ||
28 | static enum fscache_checkaux afs_vlocation_cache_check_aux( | ||
29 | void *cookie_netfs_data, const void *buffer, uint16_t buflen); | ||
30 | |||
31 | static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data, | ||
32 | void *buffer, uint16_t buflen); | ||
33 | |||
34 | static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data, | ||
35 | void *buffer, uint16_t buflen); | ||
36 | static void afs_vnode_cache_get_attr(const void *cookie_netfs_data, | ||
37 | uint64_t *size); | ||
38 | static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data, | ||
39 | void *buffer, uint16_t buflen); | ||
40 | static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data, | ||
41 | const void *buffer, | ||
42 | uint16_t buflen); | ||
43 | static void afs_vnode_cache_now_uncached(void *cookie_netfs_data); | ||
44 | |||
45 | struct fscache_netfs afs_cache_netfs = { | ||
46 | .name = "afs", | ||
47 | .version = 0, | ||
48 | }; | ||
49 | |||
50 | struct fscache_cookie_def afs_cell_cache_index_def = { | ||
51 | .name = "AFS.cell", | ||
52 | .type = FSCACHE_COOKIE_TYPE_INDEX, | ||
53 | .get_key = afs_cell_cache_get_key, | ||
54 | .get_aux = afs_cell_cache_get_aux, | ||
55 | .check_aux = afs_cell_cache_check_aux, | ||
56 | }; | ||
57 | |||
58 | struct fscache_cookie_def afs_vlocation_cache_index_def = { | ||
59 | .name = "AFS.vldb", | ||
60 | .type = FSCACHE_COOKIE_TYPE_INDEX, | ||
61 | .get_key = afs_vlocation_cache_get_key, | ||
62 | .get_aux = afs_vlocation_cache_get_aux, | ||
63 | .check_aux = afs_vlocation_cache_check_aux, | ||
64 | }; | ||
65 | |||
66 | struct fscache_cookie_def afs_volume_cache_index_def = { | ||
67 | .name = "AFS.volume", | ||
68 | .type = FSCACHE_COOKIE_TYPE_INDEX, | ||
69 | .get_key = afs_volume_cache_get_key, | ||
70 | }; | ||
71 | |||
72 | struct fscache_cookie_def afs_vnode_cache_index_def = { | ||
73 | .name = "AFS.vnode", | ||
74 | .type = FSCACHE_COOKIE_TYPE_DATAFILE, | ||
75 | .get_key = afs_vnode_cache_get_key, | ||
76 | .get_attr = afs_vnode_cache_get_attr, | ||
77 | .get_aux = afs_vnode_cache_get_aux, | ||
78 | .check_aux = afs_vnode_cache_check_aux, | ||
79 | .now_uncached = afs_vnode_cache_now_uncached, | ||
23 | }; | 80 | }; |
24 | #endif | ||
25 | 81 | ||
26 | /* | 82 | /* |
27 | * match a cell record obtained from the cache | 83 | * set the key for the index entry |
28 | */ | 84 | */ |
29 | #ifdef AFS_CACHING_SUPPORT | 85 | static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data, |
30 | static cachefs_match_val_t afs_cell_cache_match(void *target, | 86 | void *buffer, uint16_t bufmax) |
31 | const void *entry) | ||
32 | { | 87 | { |
33 | const struct afs_cache_cell *ccell = entry; | 88 | const struct afs_cell *cell = cookie_netfs_data; |
34 | struct afs_cell *cell = target; | 89 | uint16_t klen; |
35 | 90 | ||
36 | _enter("{%s},{%s}", ccell->name, cell->name); | 91 | _enter("%p,%p,%u", cell, buffer, bufmax); |
37 | 92 | ||
38 | if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) { | 93 | klen = strlen(cell->name); |
39 | _leave(" = SUCCESS"); | 94 | if (klen > bufmax) |
40 | return CACHEFS_MATCH_SUCCESS; | 95 | return 0; |
41 | } | ||
42 | 96 | ||
43 | _leave(" = FAILED"); | 97 | memcpy(buffer, cell->name, klen); |
44 | return CACHEFS_MATCH_FAILED; | 98 | return klen; |
45 | } | 99 | } |
46 | #endif | ||
47 | 100 | ||
48 | /* | 101 | /* |
49 | * update a cell record in the cache | 102 | * provide new auxilliary cache data |
50 | */ | 103 | */ |
51 | #ifdef AFS_CACHING_SUPPORT | 104 | static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data, |
52 | static void afs_cell_cache_update(void *source, void *entry) | 105 | void *buffer, uint16_t bufmax) |
53 | { | 106 | { |
54 | struct afs_cache_cell *ccell = entry; | 107 | const struct afs_cell *cell = cookie_netfs_data; |
55 | struct afs_cell *cell = source; | 108 | uint16_t dlen; |
56 | 109 | ||
57 | _enter("%p,%p", source, entry); | 110 | _enter("%p,%p,%u", cell, buffer, bufmax); |
58 | 111 | ||
59 | strncpy(ccell->name, cell->name, sizeof(ccell->name)); | 112 | dlen = cell->vl_naddrs * sizeof(cell->vl_addrs[0]); |
113 | dlen = min(dlen, bufmax); | ||
114 | dlen &= ~(sizeof(cell->vl_addrs[0]) - 1); | ||
60 | 115 | ||
61 | memcpy(ccell->vl_servers, | 116 | memcpy(buffer, cell->vl_addrs, dlen); |
62 | cell->vl_addrs, | 117 | return dlen; |
63 | min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs))); | 118 | } |
64 | 119 | ||
120 | /* | ||
121 | * check that the auxilliary data indicates that the entry is still valid | ||
122 | */ | ||
123 | static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data, | ||
124 | const void *buffer, | ||
125 | uint16_t buflen) | ||
126 | { | ||
127 | _leave(" = OKAY"); | ||
128 | return FSCACHE_CHECKAUX_OKAY; | ||
65 | } | 129 | } |
66 | #endif | ||
67 | |||
68 | #ifdef AFS_CACHING_SUPPORT | ||
69 | static cachefs_match_val_t afs_vlocation_cache_match(void *target, | ||
70 | const void *entry); | ||
71 | static void afs_vlocation_cache_update(void *source, void *entry); | ||
72 | |||
73 | struct cachefs_index_def afs_vlocation_cache_index_def = { | ||
74 | .name = "vldb", | ||
75 | .data_size = sizeof(struct afs_cache_vlocation), | ||
76 | .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, | ||
77 | .match = afs_vlocation_cache_match, | ||
78 | .update = afs_vlocation_cache_update, | ||
79 | }; | ||
80 | #endif | ||
81 | 130 | ||
131 | /*****************************************************************************/ | ||
82 | /* | 132 | /* |
83 | * match a VLDB record stored in the cache | 133 | * set the key for the index entry |
84 | * - may also load target from entry | ||
85 | */ | 134 | */ |
86 | #ifdef AFS_CACHING_SUPPORT | 135 | static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data, |
87 | static cachefs_match_val_t afs_vlocation_cache_match(void *target, | 136 | void *buffer, uint16_t bufmax) |
88 | const void *entry) | ||
89 | { | 137 | { |
90 | const struct afs_cache_vlocation *vldb = entry; | 138 | const struct afs_vlocation *vlocation = cookie_netfs_data; |
91 | struct afs_vlocation *vlocation = target; | 139 | uint16_t klen; |
140 | |||
141 | _enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax); | ||
142 | |||
143 | klen = strnlen(vlocation->vldb.name, sizeof(vlocation->vldb.name)); | ||
144 | if (klen > bufmax) | ||
145 | return 0; | ||
92 | 146 | ||
93 | _enter("{%s},{%s}", vlocation->vldb.name, vldb->name); | 147 | memcpy(buffer, vlocation->vldb.name, klen); |
94 | 148 | ||
95 | if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0 | 149 | _leave(" = %u", klen); |
96 | ) { | 150 | return klen; |
97 | if (!vlocation->valid || | 151 | } |
98 | vlocation->vldb.rtime == vldb->rtime | 152 | |
153 | /* | ||
154 | * provide new auxilliary cache data | ||
155 | */ | ||
156 | static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data, | ||
157 | void *buffer, uint16_t bufmax) | ||
158 | { | ||
159 | const struct afs_vlocation *vlocation = cookie_netfs_data; | ||
160 | uint16_t dlen; | ||
161 | |||
162 | _enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax); | ||
163 | |||
164 | dlen = sizeof(struct afs_cache_vlocation); | ||
165 | dlen -= offsetof(struct afs_cache_vlocation, nservers); | ||
166 | if (dlen > bufmax) | ||
167 | return 0; | ||
168 | |||
169 | memcpy(buffer, (uint8_t *)&vlocation->vldb.nservers, dlen); | ||
170 | |||
171 | _leave(" = %u", dlen); | ||
172 | return dlen; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * check that the auxilliary data indicates that the entry is still valid | ||
177 | */ | ||
178 | static | ||
179 | enum fscache_checkaux afs_vlocation_cache_check_aux(void *cookie_netfs_data, | ||
180 | const void *buffer, | ||
181 | uint16_t buflen) | ||
182 | { | ||
183 | const struct afs_cache_vlocation *cvldb; | ||
184 | struct afs_vlocation *vlocation = cookie_netfs_data; | ||
185 | uint16_t dlen; | ||
186 | |||
187 | _enter("{%s},%p,%u", vlocation->vldb.name, buffer, buflen); | ||
188 | |||
189 | /* check the size of the data is what we're expecting */ | ||
190 | dlen = sizeof(struct afs_cache_vlocation); | ||
191 | dlen -= offsetof(struct afs_cache_vlocation, nservers); | ||
192 | if (dlen != buflen) | ||
193 | return FSCACHE_CHECKAUX_OBSOLETE; | ||
194 | |||
195 | cvldb = container_of(buffer, struct afs_cache_vlocation, nservers); | ||
196 | |||
197 | /* if what's on disk is more valid than what's in memory, then use the | ||
198 | * VL record from the cache */ | ||
199 | if (!vlocation->valid || vlocation->vldb.rtime == cvldb->rtime) { | ||
200 | memcpy((uint8_t *)&vlocation->vldb.nservers, buffer, dlen); | ||
201 | vlocation->valid = 1; | ||
202 | _leave(" = SUCCESS [c->m]"); | ||
203 | return FSCACHE_CHECKAUX_OKAY; | ||
204 | } | ||
205 | |||
206 | /* need to update the cache if the cached info differs */ | ||
207 | if (memcmp(&vlocation->vldb, buffer, dlen) != 0) { | ||
208 | /* delete if the volume IDs for this name differ */ | ||
209 | if (memcmp(&vlocation->vldb.vid, &cvldb->vid, | ||
210 | sizeof(cvldb->vid)) != 0 | ||
99 | ) { | 211 | ) { |
100 | vlocation->vldb = *vldb; | 212 | _leave(" = OBSOLETE"); |
101 | vlocation->valid = 1; | 213 | return FSCACHE_CHECKAUX_OBSOLETE; |
102 | _leave(" = SUCCESS [c->m]"); | ||
103 | return CACHEFS_MATCH_SUCCESS; | ||
104 | } else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) { | ||
105 | /* delete if VIDs for this name differ */ | ||
106 | if (memcmp(&vlocation->vldb.vid, | ||
107 | &vldb->vid, | ||
108 | sizeof(vldb->vid)) != 0) { | ||
109 | _leave(" = DELETE"); | ||
110 | return CACHEFS_MATCH_SUCCESS_DELETE; | ||
111 | } | ||
112 | |||
113 | _leave(" = UPDATE"); | ||
114 | return CACHEFS_MATCH_SUCCESS_UPDATE; | ||
115 | } else { | ||
116 | _leave(" = SUCCESS"); | ||
117 | return CACHEFS_MATCH_SUCCESS; | ||
118 | } | 214 | } |
215 | |||
216 | _leave(" = UPDATE"); | ||
217 | return FSCACHE_CHECKAUX_NEEDS_UPDATE; | ||
119 | } | 218 | } |
120 | 219 | ||
121 | _leave(" = FAILED"); | 220 | _leave(" = OKAY"); |
122 | return CACHEFS_MATCH_FAILED; | 221 | return FSCACHE_CHECKAUX_OKAY; |
123 | } | 222 | } |
124 | #endif | ||
125 | 223 | ||
224 | /*****************************************************************************/ | ||
126 | /* | 225 | /* |
127 | * update a VLDB record stored in the cache | 226 | * set the key for the volume index entry |
128 | */ | 227 | */ |
129 | #ifdef AFS_CACHING_SUPPORT | 228 | static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data, |
130 | static void afs_vlocation_cache_update(void *source, void *entry) | 229 | void *buffer, uint16_t bufmax) |
131 | { | 230 | { |
132 | struct afs_cache_vlocation *vldb = entry; | 231 | const struct afs_volume *volume = cookie_netfs_data; |
133 | struct afs_vlocation *vlocation = source; | 232 | uint16_t klen; |
233 | |||
234 | _enter("{%u},%p,%u", volume->type, buffer, bufmax); | ||
235 | |||
236 | klen = sizeof(volume->type); | ||
237 | if (klen > bufmax) | ||
238 | return 0; | ||
134 | 239 | ||
135 | _enter(""); | 240 | memcpy(buffer, &volume->type, sizeof(volume->type)); |
241 | |||
242 | _leave(" = %u", klen); | ||
243 | return klen; | ||
136 | 244 | ||
137 | *vldb = vlocation->vldb; | ||
138 | } | 245 | } |
139 | #endif | ||
140 | |||
141 | #ifdef AFS_CACHING_SUPPORT | ||
142 | static cachefs_match_val_t afs_volume_cache_match(void *target, | ||
143 | const void *entry); | ||
144 | static void afs_volume_cache_update(void *source, void *entry); | ||
145 | |||
146 | struct cachefs_index_def afs_volume_cache_index_def = { | ||
147 | .name = "volume", | ||
148 | .data_size = sizeof(struct afs_cache_vhash), | ||
149 | .keys[0] = { CACHEFS_INDEX_KEYS_BIN, 1 }, | ||
150 | .keys[1] = { CACHEFS_INDEX_KEYS_BIN, 1 }, | ||
151 | .match = afs_volume_cache_match, | ||
152 | .update = afs_volume_cache_update, | ||
153 | }; | ||
154 | #endif | ||
155 | 246 | ||
247 | /*****************************************************************************/ | ||
156 | /* | 248 | /* |
157 | * match a volume hash record stored in the cache | 249 | * set the key for the index entry |
158 | */ | 250 | */ |
159 | #ifdef AFS_CACHING_SUPPORT | 251 | static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data, |
160 | static cachefs_match_val_t afs_volume_cache_match(void *target, | 252 | void *buffer, uint16_t bufmax) |
161 | const void *entry) | ||
162 | { | 253 | { |
163 | const struct afs_cache_vhash *vhash = entry; | 254 | const struct afs_vnode *vnode = cookie_netfs_data; |
164 | struct afs_volume *volume = target; | 255 | uint16_t klen; |
165 | 256 | ||
166 | _enter("{%u},{%u}", volume->type, vhash->vtype); | 257 | _enter("{%x,%x,%llx},%p,%u", |
258 | vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version, | ||
259 | buffer, bufmax); | ||
167 | 260 | ||
168 | if (volume->type == vhash->vtype) { | 261 | klen = sizeof(vnode->fid.vnode); |
169 | _leave(" = SUCCESS"); | 262 | if (klen > bufmax) |
170 | return CACHEFS_MATCH_SUCCESS; | 263 | return 0; |
171 | } | 264 | |
265 | memcpy(buffer, &vnode->fid.vnode, sizeof(vnode->fid.vnode)); | ||
172 | 266 | ||
173 | _leave(" = FAILED"); | 267 | _leave(" = %u", klen); |
174 | return CACHEFS_MATCH_FAILED; | 268 | return klen; |
175 | } | 269 | } |
176 | #endif | ||
177 | 270 | ||
178 | /* | 271 | /* |
179 | * update a volume hash record stored in the cache | 272 | * provide updated file attributes |
180 | */ | 273 | */ |
181 | #ifdef AFS_CACHING_SUPPORT | 274 | static void afs_vnode_cache_get_attr(const void *cookie_netfs_data, |
182 | static void afs_volume_cache_update(void *source, void *entry) | 275 | uint64_t *size) |
183 | { | 276 | { |
184 | struct afs_cache_vhash *vhash = entry; | 277 | const struct afs_vnode *vnode = cookie_netfs_data; |
185 | struct afs_volume *volume = source; | ||
186 | 278 | ||
187 | _enter(""); | 279 | _enter("{%x,%x,%llx},", |
280 | vnode->fid.vnode, vnode->fid.unique, | ||
281 | vnode->status.data_version); | ||
188 | 282 | ||
189 | vhash->vtype = volume->type; | 283 | *size = vnode->status.size; |
190 | } | 284 | } |
191 | #endif | ||
192 | |||
193 | #ifdef AFS_CACHING_SUPPORT | ||
194 | static cachefs_match_val_t afs_vnode_cache_match(void *target, | ||
195 | const void *entry); | ||
196 | static void afs_vnode_cache_update(void *source, void *entry); | ||
197 | |||
198 | struct cachefs_index_def afs_vnode_cache_index_def = { | ||
199 | .name = "vnode", | ||
200 | .data_size = sizeof(struct afs_cache_vnode), | ||
201 | .keys[0] = { CACHEFS_INDEX_KEYS_BIN, 4 }, | ||
202 | .match = afs_vnode_cache_match, | ||
203 | .update = afs_vnode_cache_update, | ||
204 | }; | ||
205 | #endif | ||
206 | 285 | ||
207 | /* | 286 | /* |
208 | * match a vnode record stored in the cache | 287 | * provide new auxilliary cache data |
288 | */ | ||
289 | static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data, | ||
290 | void *buffer, uint16_t bufmax) | ||
291 | { | ||
292 | const struct afs_vnode *vnode = cookie_netfs_data; | ||
293 | uint16_t dlen; | ||
294 | |||
295 | _enter("{%x,%x,%Lx},%p,%u", | ||
296 | vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version, | ||
297 | buffer, bufmax); | ||
298 | |||
299 | dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version); | ||
300 | if (dlen > bufmax) | ||
301 | return 0; | ||
302 | |||
303 | memcpy(buffer, &vnode->fid.unique, sizeof(vnode->fid.unique)); | ||
304 | buffer += sizeof(vnode->fid.unique); | ||
305 | memcpy(buffer, &vnode->status.data_version, | ||
306 | sizeof(vnode->status.data_version)); | ||
307 | |||
308 | _leave(" = %u", dlen); | ||
309 | return dlen; | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * check that the auxilliary data indicates that the entry is still valid | ||
209 | */ | 314 | */ |
210 | #ifdef AFS_CACHING_SUPPORT | 315 | static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data, |
211 | static cachefs_match_val_t afs_vnode_cache_match(void *target, | 316 | const void *buffer, |
212 | const void *entry) | 317 | uint16_t buflen) |
213 | { | 318 | { |
214 | const struct afs_cache_vnode *cvnode = entry; | 319 | struct afs_vnode *vnode = cookie_netfs_data; |
215 | struct afs_vnode *vnode = target; | 320 | uint16_t dlen; |
216 | 321 | ||
217 | _enter("{%x,%x,%Lx},{%x,%x,%Lx}", | 322 | _enter("{%x,%x,%llx},%p,%u", |
218 | vnode->fid.vnode, | 323 | vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version, |
219 | vnode->fid.unique, | 324 | buffer, buflen); |
220 | vnode->status.version, | 325 | |
221 | cvnode->vnode_id, | 326 | /* check the size of the data is what we're expecting */ |
222 | cvnode->vnode_unique, | 327 | dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version); |
223 | cvnode->data_version); | 328 | if (dlen != buflen) { |
224 | 329 | _leave(" = OBSOLETE [len %hx != %hx]", dlen, buflen); | |
225 | if (vnode->fid.vnode != cvnode->vnode_id) { | 330 | return FSCACHE_CHECKAUX_OBSOLETE; |
226 | _leave(" = FAILED"); | ||
227 | return CACHEFS_MATCH_FAILED; | ||
228 | } | 331 | } |
229 | 332 | ||
230 | if (vnode->fid.unique != cvnode->vnode_unique || | 333 | if (memcmp(buffer, |
231 | vnode->status.version != cvnode->data_version) { | 334 | &vnode->fid.unique, |
232 | _leave(" = DELETE"); | 335 | sizeof(vnode->fid.unique) |
233 | return CACHEFS_MATCH_SUCCESS_DELETE; | 336 | ) != 0) { |
337 | unsigned unique; | ||
338 | |||
339 | memcpy(&unique, buffer, sizeof(unique)); | ||
340 | |||
341 | _leave(" = OBSOLETE [uniq %x != %x]", | ||
342 | unique, vnode->fid.unique); | ||
343 | return FSCACHE_CHECKAUX_OBSOLETE; | ||
344 | } | ||
345 | |||
346 | if (memcmp(buffer + sizeof(vnode->fid.unique), | ||
347 | &vnode->status.data_version, | ||
348 | sizeof(vnode->status.data_version) | ||
349 | ) != 0) { | ||
350 | afs_dataversion_t version; | ||
351 | |||
352 | memcpy(&version, buffer + sizeof(vnode->fid.unique), | ||
353 | sizeof(version)); | ||
354 | |||
355 | _leave(" = OBSOLETE [vers %llx != %llx]", | ||
356 | version, vnode->status.data_version); | ||
357 | return FSCACHE_CHECKAUX_OBSOLETE; | ||
234 | } | 358 | } |
235 | 359 | ||
236 | _leave(" = SUCCESS"); | 360 | _leave(" = SUCCESS"); |
237 | return CACHEFS_MATCH_SUCCESS; | 361 | return FSCACHE_CHECKAUX_OKAY; |
238 | } | 362 | } |
239 | #endif | ||
240 | 363 | ||
241 | /* | 364 | /* |
242 | * update a vnode record stored in the cache | 365 | * indication the cookie is no longer uncached |
366 | * - this function is called when the backing store currently caching a cookie | ||
367 | * is removed | ||
368 | * - the netfs should use this to clean up any markers indicating cached pages | ||
369 | * - this is mandatory for any object that may have data | ||
243 | */ | 370 | */ |
244 | #ifdef AFS_CACHING_SUPPORT | 371 | static void afs_vnode_cache_now_uncached(void *cookie_netfs_data) |
245 | static void afs_vnode_cache_update(void *source, void *entry) | ||
246 | { | 372 | { |
247 | struct afs_cache_vnode *cvnode = entry; | 373 | struct afs_vnode *vnode = cookie_netfs_data; |
248 | struct afs_vnode *vnode = source; | 374 | struct pagevec pvec; |
375 | pgoff_t first; | ||
376 | int loop, nr_pages; | ||
377 | |||
378 | _enter("{%x,%x,%Lx}", | ||
379 | vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version); | ||
380 | |||
381 | pagevec_init(&pvec, 0); | ||
382 | first = 0; | ||
383 | |||
384 | for (;;) { | ||
385 | /* grab a bunch of pages to clean */ | ||
386 | nr_pages = pagevec_lookup(&pvec, vnode->vfs_inode.i_mapping, | ||
387 | first, | ||
388 | PAGEVEC_SIZE - pagevec_count(&pvec)); | ||
389 | if (!nr_pages) | ||
390 | break; | ||
249 | 391 | ||
250 | _enter(""); | 392 | for (loop = 0; loop < nr_pages; loop++) |
393 | ClearPageFsCache(pvec.pages[loop]); | ||
394 | |||
395 | first = pvec.pages[nr_pages - 1]->index + 1; | ||
396 | |||
397 | pvec.nr = nr_pages; | ||
398 | pagevec_release(&pvec); | ||
399 | cond_resched(); | ||
400 | } | ||
251 | 401 | ||
252 | cvnode->vnode_id = vnode->fid.vnode; | 402 | _leave(""); |
253 | cvnode->vnode_unique = vnode->fid.unique; | ||
254 | cvnode->data_version = vnode->status.version; | ||
255 | } | 403 | } |
256 | #endif | ||
diff --git a/fs/afs/cache.h b/fs/afs/cache.h index 36a3642cf90e..5c4f6b499e90 100644 --- a/fs/afs/cache.h +++ b/fs/afs/cache.h | |||
@@ -1,6 +1,6 @@ | |||
1 | /* AFS local cache management interface | 1 | /* AFS local cache management interface |
2 | * | 2 | * |
3 | * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. |
4 | * Written by David Howells (dhowells@redhat.com) | 4 | * Written by David Howells (dhowells@redhat.com) |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
@@ -9,15 +9,4 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #ifndef AFS_CACHE_H | 12 | #include <linux/fscache.h> |
13 | #define AFS_CACHE_H | ||
14 | |||
15 | #undef AFS_CACHING_SUPPORT | ||
16 | |||
17 | #include <linux/mm.h> | ||
18 | #ifdef AFS_CACHING_SUPPORT | ||
19 | #include <linux/cachefs.h> | ||
20 | #endif | ||
21 | #include "types.h" | ||
22 | |||
23 | #endif /* AFS_CACHE_H */ | ||
diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 5e1df14e16b1..e19c13f059ed 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c | |||
@@ -147,12 +147,11 @@ struct afs_cell *afs_cell_create(const char *name, char *vllist) | |||
147 | if (ret < 0) | 147 | if (ret < 0) |
148 | goto error; | 148 | goto error; |
149 | 149 | ||
150 | #ifdef AFS_CACHING_SUPPORT | 150 | #ifdef CONFIG_AFS_FSCACHE |
151 | /* put it up for caching */ | 151 | /* put it up for caching (this never returns an error) */ |
152 | cachefs_acquire_cookie(afs_cache_netfs.primary_index, | 152 | cell->cache = fscache_acquire_cookie(afs_cache_netfs.primary_index, |
153 | &afs_vlocation_cache_index_def, | 153 | &afs_cell_cache_index_def, |
154 | cell, | 154 | cell); |
155 | &cell->cache); | ||
156 | #endif | 155 | #endif |
157 | 156 | ||
158 | /* add to the cell lists */ | 157 | /* add to the cell lists */ |
@@ -362,10 +361,9 @@ static void afs_cell_destroy(struct afs_cell *cell) | |||
362 | list_del_init(&cell->proc_link); | 361 | list_del_init(&cell->proc_link); |
363 | up_write(&afs_proc_cells_sem); | 362 | up_write(&afs_proc_cells_sem); |
364 | 363 | ||
365 | #ifdef AFS_CACHING_SUPPORT | 364 | #ifdef CONFIG_AFS_FSCACHE |
366 | cachefs_relinquish_cookie(cell->cache, 0); | 365 | fscache_relinquish_cookie(cell->cache, 0); |
367 | #endif | 366 | #endif |
368 | |||
369 | key_put(cell->anonymous_key); | 367 | key_put(cell->anonymous_key); |
370 | kfree(cell); | 368 | kfree(cell); |
371 | 369 | ||
diff --git a/fs/afs/file.c b/fs/afs/file.c index a3901769a96c..7a1d942ef68d 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c | |||
@@ -23,6 +23,9 @@ static void afs_invalidatepage(struct page *page, unsigned long offset); | |||
23 | static int afs_releasepage(struct page *page, gfp_t gfp_flags); | 23 | static int afs_releasepage(struct page *page, gfp_t gfp_flags); |
24 | static int afs_launder_page(struct page *page); | 24 | static int afs_launder_page(struct page *page); |
25 | 25 | ||
26 | static int afs_readpages(struct file *filp, struct address_space *mapping, | ||
27 | struct list_head *pages, unsigned nr_pages); | ||
28 | |||
26 | const struct file_operations afs_file_operations = { | 29 | const struct file_operations afs_file_operations = { |
27 | .open = afs_open, | 30 | .open = afs_open, |
28 | .release = afs_release, | 31 | .release = afs_release, |
@@ -46,6 +49,7 @@ const struct inode_operations afs_file_inode_operations = { | |||
46 | 49 | ||
47 | const struct address_space_operations afs_fs_aops = { | 50 | const struct address_space_operations afs_fs_aops = { |
48 | .readpage = afs_readpage, | 51 | .readpage = afs_readpage, |
52 | .readpages = afs_readpages, | ||
49 | .set_page_dirty = afs_set_page_dirty, | 53 | .set_page_dirty = afs_set_page_dirty, |
50 | .launder_page = afs_launder_page, | 54 | .launder_page = afs_launder_page, |
51 | .releasepage = afs_releasepage, | 55 | .releasepage = afs_releasepage, |
@@ -101,37 +105,18 @@ int afs_release(struct inode *inode, struct file *file) | |||
101 | /* | 105 | /* |
102 | * deal with notification that a page was read from the cache | 106 | * deal with notification that a page was read from the cache |
103 | */ | 107 | */ |
104 | #ifdef AFS_CACHING_SUPPORT | 108 | static void afs_file_readpage_read_complete(struct page *page, |
105 | static void afs_readpage_read_complete(void *cookie_data, | 109 | void *data, |
106 | struct page *page, | 110 | int error) |
107 | void *data, | ||
108 | int error) | ||
109 | { | 111 | { |
110 | _enter("%p,%p,%p,%d", cookie_data, page, data, error); | 112 | _enter("%p,%p,%d", page, data, error); |
111 | 113 | ||
112 | if (error) | 114 | /* if the read completes with an error, we just unlock the page and let |
113 | SetPageError(page); | 115 | * the VM reissue the readpage */ |
114 | else | 116 | if (!error) |
115 | SetPageUptodate(page); | 117 | SetPageUptodate(page); |
116 | unlock_page(page); | 118 | unlock_page(page); |
117 | |||
118 | } | 119 | } |
119 | #endif | ||
120 | |||
121 | /* | ||
122 | * deal with notification that a page was written to the cache | ||
123 | */ | ||
124 | #ifdef AFS_CACHING_SUPPORT | ||
125 | static void afs_readpage_write_complete(void *cookie_data, | ||
126 | struct page *page, | ||
127 | void *data, | ||
128 | int error) | ||
129 | { | ||
130 | _enter("%p,%p,%p,%d", cookie_data, page, data, error); | ||
131 | |||
132 | unlock_page(page); | ||
133 | } | ||
134 | #endif | ||
135 | 120 | ||
136 | /* | 121 | /* |
137 | * AFS read page from file, directory or symlink | 122 | * AFS read page from file, directory or symlink |
@@ -161,9 +146,9 @@ static int afs_readpage(struct file *file, struct page *page) | |||
161 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) | 146 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) |
162 | goto error; | 147 | goto error; |
163 | 148 | ||
164 | #ifdef AFS_CACHING_SUPPORT | ||
165 | /* is it cached? */ | 149 | /* is it cached? */ |
166 | ret = cachefs_read_or_alloc_page(vnode->cache, | 150 | #ifdef CONFIG_AFS_FSCACHE |
151 | ret = fscache_read_or_alloc_page(vnode->cache, | ||
167 | page, | 152 | page, |
168 | afs_file_readpage_read_complete, | 153 | afs_file_readpage_read_complete, |
169 | NULL, | 154 | NULL, |
@@ -171,20 +156,21 @@ static int afs_readpage(struct file *file, struct page *page) | |||
171 | #else | 156 | #else |
172 | ret = -ENOBUFS; | 157 | ret = -ENOBUFS; |
173 | #endif | 158 | #endif |
174 | |||
175 | switch (ret) { | 159 | switch (ret) { |
176 | /* read BIO submitted and wb-journal entry found */ | ||
177 | case 1: | ||
178 | BUG(); // TODO - handle wb-journal match | ||
179 | |||
180 | /* read BIO submitted (page in cache) */ | 160 | /* read BIO submitted (page in cache) */ |
181 | case 0: | 161 | case 0: |
182 | break; | 162 | break; |
183 | 163 | ||
184 | /* no page available in cache */ | 164 | /* page not yet cached */ |
185 | case -ENOBUFS: | ||
186 | case -ENODATA: | 165 | case -ENODATA: |
166 | _debug("cache said ENODATA"); | ||
167 | goto go_on; | ||
168 | |||
169 | /* page will not be cached */ | ||
170 | case -ENOBUFS: | ||
171 | _debug("cache said ENOBUFS"); | ||
187 | default: | 172 | default: |
173 | go_on: | ||
188 | offset = page->index << PAGE_CACHE_SHIFT; | 174 | offset = page->index << PAGE_CACHE_SHIFT; |
189 | len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE); | 175 | len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE); |
190 | 176 | ||
@@ -198,27 +184,25 @@ static int afs_readpage(struct file *file, struct page *page) | |||
198 | set_bit(AFS_VNODE_DELETED, &vnode->flags); | 184 | set_bit(AFS_VNODE_DELETED, &vnode->flags); |
199 | ret = -ESTALE; | 185 | ret = -ESTALE; |
200 | } | 186 | } |
201 | #ifdef AFS_CACHING_SUPPORT | 187 | |
202 | cachefs_uncache_page(vnode->cache, page); | 188 | #ifdef CONFIG_AFS_FSCACHE |
189 | fscache_uncache_page(vnode->cache, page); | ||
203 | #endif | 190 | #endif |
191 | BUG_ON(PageFsCache(page)); | ||
204 | goto error; | 192 | goto error; |
205 | } | 193 | } |
206 | 194 | ||
207 | SetPageUptodate(page); | 195 | SetPageUptodate(page); |
208 | 196 | ||
209 | #ifdef AFS_CACHING_SUPPORT | 197 | /* send the page to the cache */ |
210 | if (cachefs_write_page(vnode->cache, | 198 | #ifdef CONFIG_AFS_FSCACHE |
211 | page, | 199 | if (PageFsCache(page) && |
212 | afs_file_readpage_write_complete, | 200 | fscache_write_page(vnode->cache, page, GFP_KERNEL) != 0) { |
213 | NULL, | 201 | fscache_uncache_page(vnode->cache, page); |
214 | GFP_KERNEL) != 0 | 202 | BUG_ON(PageFsCache(page)); |
215 | ) { | ||
216 | cachefs_uncache_page(vnode->cache, page); | ||
217 | unlock_page(page); | ||
218 | } | 203 | } |
219 | #else | ||
220 | unlock_page(page); | ||
221 | #endif | 204 | #endif |
205 | unlock_page(page); | ||
222 | } | 206 | } |
223 | 207 | ||
224 | _leave(" = 0"); | 208 | _leave(" = 0"); |
@@ -232,34 +216,59 @@ error: | |||
232 | } | 216 | } |
233 | 217 | ||
234 | /* | 218 | /* |
235 | * invalidate part or all of a page | 219 | * read a set of pages |
236 | */ | 220 | */ |
237 | static void afs_invalidatepage(struct page *page, unsigned long offset) | 221 | static int afs_readpages(struct file *file, struct address_space *mapping, |
222 | struct list_head *pages, unsigned nr_pages) | ||
238 | { | 223 | { |
239 | int ret = 1; | 224 | struct afs_vnode *vnode; |
225 | int ret = 0; | ||
240 | 226 | ||
241 | _enter("{%lu},%lu", page->index, offset); | 227 | _enter(",{%lu},,%d", mapping->host->i_ino, nr_pages); |
242 | 228 | ||
243 | BUG_ON(!PageLocked(page)); | 229 | vnode = AFS_FS_I(mapping->host); |
230 | if (vnode->flags & AFS_VNODE_DELETED) { | ||
231 | _leave(" = -ESTALE"); | ||
232 | return -ESTALE; | ||
233 | } | ||
244 | 234 | ||
245 | if (PagePrivate(page)) { | 235 | /* attempt to read as many of the pages as possible */ |
246 | /* We release buffers only if the entire page is being | 236 | #ifdef CONFIG_AFS_FSCACHE |
247 | * invalidated. | 237 | ret = fscache_read_or_alloc_pages(vnode->cache, |
248 | * The get_block cached value has been unconditionally | 238 | mapping, |
249 | * invalidated, so real IO is not possible anymore. | 239 | pages, |
250 | */ | 240 | &nr_pages, |
251 | if (offset == 0) { | 241 | afs_file_readpage_read_complete, |
252 | BUG_ON(!PageLocked(page)); | 242 | NULL, |
253 | 243 | mapping_gfp_mask(mapping)); | |
254 | ret = 0; | 244 | #else |
255 | if (!PageWriteback(page)) | 245 | ret = -ENOBUFS; |
256 | ret = page->mapping->a_ops->releasepage(page, | 246 | #endif |
257 | 0); | 247 | |
258 | /* possibly should BUG_ON(!ret); - neilb */ | 248 | switch (ret) { |
259 | } | 249 | /* all pages are being read from the cache */ |
250 | case 0: | ||
251 | BUG_ON(!list_empty(pages)); | ||
252 | BUG_ON(nr_pages != 0); | ||
253 | _leave(" = 0 [reading all]"); | ||
254 | return 0; | ||
255 | |||
256 | /* there were pages that couldn't be read from the cache */ | ||
257 | case -ENODATA: | ||
258 | case -ENOBUFS: | ||
259 | break; | ||
260 | |||
261 | /* other error */ | ||
262 | default: | ||
263 | _leave(" = %d", ret); | ||
264 | return ret; | ||
260 | } | 265 | } |
261 | 266 | ||
262 | _leave(" = %d", ret); | 267 | /* load the missing pages from the network */ |
268 | ret = read_cache_pages(mapping, pages, (void *) afs_readpage, file); | ||
269 | |||
270 | _leave(" = %d [netting]", ret); | ||
271 | return ret; | ||
263 | } | 272 | } |
264 | 273 | ||
265 | /* | 274 | /* |
@@ -273,25 +282,82 @@ static int afs_launder_page(struct page *page) | |||
273 | } | 282 | } |
274 | 283 | ||
275 | /* | 284 | /* |
276 | * release a page and cleanup its private data | 285 | * invalidate part or all of a page |
286 | * - release a page and clean up its private data if offset is 0 (indicating | ||
287 | * the entire page) | ||
288 | */ | ||
289 | static void afs_invalidatepage(struct page *page, unsigned long offset) | ||
290 | { | ||
291 | struct afs_writeback *wb = (struct afs_writeback *) page_private(page); | ||
292 | |||
293 | _enter("{%lu},%lu", page->index, offset); | ||
294 | |||
295 | BUG_ON(!PageLocked(page)); | ||
296 | |||
297 | /* we clean up only if the entire page is being invalidated */ | ||
298 | if (offset == 0) { | ||
299 | #ifdef CONFIG_AFS_FSCACHE | ||
300 | if (PageFsCache(page)) { | ||
301 | struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); | ||
302 | fscache_wait_on_page_write(vnode->cache, page); | ||
303 | fscache_uncache_page(vnode->cache, page); | ||
304 | ClearPageFsCache(page); | ||
305 | } | ||
306 | #endif | ||
307 | |||
308 | if (PagePrivate(page)) { | ||
309 | if (wb && !PageWriteback(page)) { | ||
310 | set_page_private(page, 0); | ||
311 | afs_put_writeback(wb); | ||
312 | } | ||
313 | |||
314 | if (!page_private(page)) | ||
315 | ClearPagePrivate(page); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | _leave(""); | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * release a page and clean up its private state if it's not busy | ||
324 | * - return true if the page can now be released, false if not | ||
277 | */ | 325 | */ |
278 | static int afs_releasepage(struct page *page, gfp_t gfp_flags) | 326 | static int afs_releasepage(struct page *page, gfp_t gfp_flags) |
279 | { | 327 | { |
328 | struct afs_writeback *wb = (struct afs_writeback *) page_private(page); | ||
280 | struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); | 329 | struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); |
281 | struct afs_writeback *wb; | ||
282 | 330 | ||
283 | _enter("{{%x:%u}[%lu],%lx},%x", | 331 | _enter("{{%x:%u}[%lu],%lx},%x", |
284 | vnode->fid.vid, vnode->fid.vnode, page->index, page->flags, | 332 | vnode->fid.vid, vnode->fid.vnode, page->index, page->flags, |
285 | gfp_flags); | 333 | gfp_flags); |
286 | 334 | ||
335 | /* deny if page is being written to the cache and the caller hasn't | ||
336 | * elected to wait */ | ||
337 | #ifdef CONFIG_AFS_FSCACHE | ||
338 | if (PageFsCache(page)) { | ||
339 | if (fscache_check_page_write(vnode->cache, page)) { | ||
340 | if (!(gfp_flags & __GFP_WAIT)) { | ||
341 | _leave(" = F [cache busy]"); | ||
342 | return 0; | ||
343 | } | ||
344 | fscache_wait_on_page_write(vnode->cache, page); | ||
345 | } | ||
346 | |||
347 | fscache_uncache_page(vnode->cache, page); | ||
348 | ClearPageFsCache(page); | ||
349 | } | ||
350 | #endif | ||
351 | |||
287 | if (PagePrivate(page)) { | 352 | if (PagePrivate(page)) { |
288 | wb = (struct afs_writeback *) page_private(page); | 353 | if (wb) { |
289 | ASSERT(wb != NULL); | 354 | set_page_private(page, 0); |
290 | set_page_private(page, 0); | 355 | afs_put_writeback(wb); |
356 | } | ||
291 | ClearPagePrivate(page); | 357 | ClearPagePrivate(page); |
292 | afs_put_writeback(wb); | ||
293 | } | 358 | } |
294 | 359 | ||
295 | _leave(" = 0"); | 360 | /* indicate that the page can be released */ |
296 | return 0; | 361 | _leave(" = T"); |
362 | return 1; | ||
297 | } | 363 | } |
diff --git a/fs/afs/inode.c b/fs/afs/inode.c index bb47217f6a18..c048f0658751 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c | |||
@@ -61,6 +61,11 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) | |||
61 | return -EBADMSG; | 61 | return -EBADMSG; |
62 | } | 62 | } |
63 | 63 | ||
64 | #ifdef CONFIG_AFS_FSCACHE | ||
65 | if (vnode->status.size != inode->i_size) | ||
66 | fscache_attr_changed(vnode->cache); | ||
67 | #endif | ||
68 | |||
64 | inode->i_nlink = vnode->status.nlink; | 69 | inode->i_nlink = vnode->status.nlink; |
65 | inode->i_uid = vnode->status.owner; | 70 | inode->i_uid = vnode->status.owner; |
66 | inode->i_gid = 0; | 71 | inode->i_gid = 0; |
@@ -149,15 +154,6 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, | |||
149 | return inode; | 154 | return inode; |
150 | } | 155 | } |
151 | 156 | ||
152 | #ifdef AFS_CACHING_SUPPORT | ||
153 | /* set up caching before reading the status, as fetch-status reads the | ||
154 | * first page of symlinks to see if they're really mntpts */ | ||
155 | cachefs_acquire_cookie(vnode->volume->cache, | ||
156 | NULL, | ||
157 | vnode, | ||
158 | &vnode->cache); | ||
159 | #endif | ||
160 | |||
161 | if (!status) { | 157 | if (!status) { |
162 | /* it's a remotely extant inode */ | 158 | /* it's a remotely extant inode */ |
163 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | 159 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); |
@@ -183,6 +179,15 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, | |||
183 | } | 179 | } |
184 | } | 180 | } |
185 | 181 | ||
182 | /* set up caching before mapping the status, as map-status reads the | ||
183 | * first page of symlinks to see if they're really mountpoints */ | ||
184 | inode->i_size = vnode->status.size; | ||
185 | #ifdef CONFIG_AFS_FSCACHE | ||
186 | vnode->cache = fscache_acquire_cookie(vnode->volume->cache, | ||
187 | &afs_vnode_cache_index_def, | ||
188 | vnode); | ||
189 | #endif | ||
190 | |||
186 | ret = afs_inode_map_status(vnode, key); | 191 | ret = afs_inode_map_status(vnode, key); |
187 | if (ret < 0) | 192 | if (ret < 0) |
188 | goto bad_inode; | 193 | goto bad_inode; |
@@ -196,6 +201,10 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, | |||
196 | 201 | ||
197 | /* failure */ | 202 | /* failure */ |
198 | bad_inode: | 203 | bad_inode: |
204 | #ifdef CONFIG_AFS_FSCACHE | ||
205 | fscache_relinquish_cookie(vnode->cache, 0); | ||
206 | vnode->cache = NULL; | ||
207 | #endif | ||
199 | iget_failed(inode); | 208 | iget_failed(inode); |
200 | _leave(" = %d [bad]", ret); | 209 | _leave(" = %d [bad]", ret); |
201 | return ERR_PTR(ret); | 210 | return ERR_PTR(ret); |
@@ -340,8 +349,8 @@ void afs_clear_inode(struct inode *inode) | |||
340 | ASSERT(list_empty(&vnode->writebacks)); | 349 | ASSERT(list_empty(&vnode->writebacks)); |
341 | ASSERT(!vnode->cb_promised); | 350 | ASSERT(!vnode->cb_promised); |
342 | 351 | ||
343 | #ifdef AFS_CACHING_SUPPORT | 352 | #ifdef CONFIG_AFS_FSCACHE |
344 | cachefs_relinquish_cookie(vnode->cache, 0); | 353 | fscache_relinquish_cookie(vnode->cache, 0); |
345 | vnode->cache = NULL; | 354 | vnode->cache = NULL; |
346 | #endif | 355 | #endif |
347 | 356 | ||
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 67f259d99cd6..106be66dafd2 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -21,6 +21,7 @@ | |||
21 | 21 | ||
22 | #include "afs.h" | 22 | #include "afs.h" |
23 | #include "afs_vl.h" | 23 | #include "afs_vl.h" |
24 | #include "cache.h" | ||
24 | 25 | ||
25 | #define AFS_CELL_MAX_ADDRS 15 | 26 | #define AFS_CELL_MAX_ADDRS 15 |
26 | 27 | ||
@@ -193,8 +194,8 @@ struct afs_cell { | |||
193 | struct key *anonymous_key; /* anonymous user key for this cell */ | 194 | struct key *anonymous_key; /* anonymous user key for this cell */ |
194 | struct list_head proc_link; /* /proc cell list link */ | 195 | struct list_head proc_link; /* /proc cell list link */ |
195 | struct proc_dir_entry *proc_dir; /* /proc dir for this cell */ | 196 | struct proc_dir_entry *proc_dir; /* /proc dir for this cell */ |
196 | #ifdef AFS_CACHING_SUPPORT | 197 | #ifdef CONFIG_AFS_FSCACHE |
197 | struct cachefs_cookie *cache; /* caching cookie */ | 198 | struct fscache_cookie *cache; /* caching cookie */ |
198 | #endif | 199 | #endif |
199 | 200 | ||
200 | /* server record management */ | 201 | /* server record management */ |
@@ -249,8 +250,8 @@ struct afs_vlocation { | |||
249 | struct list_head grave; /* link in master graveyard list */ | 250 | struct list_head grave; /* link in master graveyard list */ |
250 | struct list_head update; /* link in master update list */ | 251 | struct list_head update; /* link in master update list */ |
251 | struct afs_cell *cell; /* cell to which volume belongs */ | 252 | struct afs_cell *cell; /* cell to which volume belongs */ |
252 | #ifdef AFS_CACHING_SUPPORT | 253 | #ifdef CONFIG_AFS_FSCACHE |
253 | struct cachefs_cookie *cache; /* caching cookie */ | 254 | struct fscache_cookie *cache; /* caching cookie */ |
254 | #endif | 255 | #endif |
255 | struct afs_cache_vlocation vldb; /* volume information DB record */ | 256 | struct afs_cache_vlocation vldb; /* volume information DB record */ |
256 | struct afs_volume *vols[3]; /* volume access record pointer (index by type) */ | 257 | struct afs_volume *vols[3]; /* volume access record pointer (index by type) */ |
@@ -302,8 +303,8 @@ struct afs_volume { | |||
302 | atomic_t usage; | 303 | atomic_t usage; |
303 | struct afs_cell *cell; /* cell to which belongs (unrefd ptr) */ | 304 | struct afs_cell *cell; /* cell to which belongs (unrefd ptr) */ |
304 | struct afs_vlocation *vlocation; /* volume location */ | 305 | struct afs_vlocation *vlocation; /* volume location */ |
305 | #ifdef AFS_CACHING_SUPPORT | 306 | #ifdef CONFIG_AFS_FSCACHE |
306 | struct cachefs_cookie *cache; /* caching cookie */ | 307 | struct fscache_cookie *cache; /* caching cookie */ |
307 | #endif | 308 | #endif |
308 | afs_volid_t vid; /* volume ID */ | 309 | afs_volid_t vid; /* volume ID */ |
309 | afs_voltype_t type; /* type of volume */ | 310 | afs_voltype_t type; /* type of volume */ |
@@ -333,8 +334,8 @@ struct afs_vnode { | |||
333 | struct afs_server *server; /* server currently supplying this file */ | 334 | struct afs_server *server; /* server currently supplying this file */ |
334 | struct afs_fid fid; /* the file identifier for this inode */ | 335 | struct afs_fid fid; /* the file identifier for this inode */ |
335 | struct afs_file_status status; /* AFS status info for this file */ | 336 | struct afs_file_status status; /* AFS status info for this file */ |
336 | #ifdef AFS_CACHING_SUPPORT | 337 | #ifdef CONFIG_AFS_FSCACHE |
337 | struct cachefs_cookie *cache; /* caching cookie */ | 338 | struct fscache_cookie *cache; /* caching cookie */ |
338 | #endif | 339 | #endif |
339 | struct afs_permits *permits; /* cache of permits so far obtained */ | 340 | struct afs_permits *permits; /* cache of permits so far obtained */ |
340 | struct mutex permits_lock; /* lock for altering permits list */ | 341 | struct mutex permits_lock; /* lock for altering permits list */ |
@@ -428,6 +429,22 @@ struct afs_uuid { | |||
428 | 429 | ||
429 | /*****************************************************************************/ | 430 | /*****************************************************************************/ |
430 | /* | 431 | /* |
432 | * cache.c | ||
433 | */ | ||
434 | #ifdef CONFIG_AFS_FSCACHE | ||
435 | extern struct fscache_netfs afs_cache_netfs; | ||
436 | extern struct fscache_cookie_def afs_cell_cache_index_def; | ||
437 | extern struct fscache_cookie_def afs_vlocation_cache_index_def; | ||
438 | extern struct fscache_cookie_def afs_volume_cache_index_def; | ||
439 | extern struct fscache_cookie_def afs_vnode_cache_index_def; | ||
440 | #else | ||
441 | #define afs_cell_cache_index_def (*(struct fscache_cookie_def *) NULL) | ||
442 | #define afs_vlocation_cache_index_def (*(struct fscache_cookie_def *) NULL) | ||
443 | #define afs_volume_cache_index_def (*(struct fscache_cookie_def *) NULL) | ||
444 | #define afs_vnode_cache_index_def (*(struct fscache_cookie_def *) NULL) | ||
445 | #endif | ||
446 | |||
447 | /* | ||
431 | * callback.c | 448 | * callback.c |
432 | */ | 449 | */ |
433 | extern void afs_init_callback_state(struct afs_server *); | 450 | extern void afs_init_callback_state(struct afs_server *); |
@@ -446,9 +463,6 @@ extern void afs_callback_update_kill(void); | |||
446 | */ | 463 | */ |
447 | extern struct rw_semaphore afs_proc_cells_sem; | 464 | extern struct rw_semaphore afs_proc_cells_sem; |
448 | extern struct list_head afs_proc_cells; | 465 | extern struct list_head afs_proc_cells; |
449 | #ifdef AFS_CACHING_SUPPORT | ||
450 | extern struct cachefs_index_def afs_cache_cell_index_def; | ||
451 | #endif | ||
452 | 466 | ||
453 | #define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0) | 467 | #define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0) |
454 | extern int afs_cell_init(char *); | 468 | extern int afs_cell_init(char *); |
@@ -554,9 +568,6 @@ extern void afs_clear_inode(struct inode *); | |||
554 | * main.c | 568 | * main.c |
555 | */ | 569 | */ |
556 | extern struct afs_uuid afs_uuid; | 570 | extern struct afs_uuid afs_uuid; |
557 | #ifdef AFS_CACHING_SUPPORT | ||
558 | extern struct cachefs_netfs afs_cache_netfs; | ||
559 | #endif | ||
560 | 571 | ||
561 | /* | 572 | /* |
562 | * misc.c | 573 | * misc.c |
@@ -637,10 +648,6 @@ extern int afs_get_MAC_address(u8 *, size_t); | |||
637 | /* | 648 | /* |
638 | * vlclient.c | 649 | * vlclient.c |
639 | */ | 650 | */ |
640 | #ifdef AFS_CACHING_SUPPORT | ||
641 | extern struct cachefs_index_def afs_vlocation_cache_index_def; | ||
642 | #endif | ||
643 | |||
644 | extern int afs_vl_get_entry_by_name(struct in_addr *, struct key *, | 651 | extern int afs_vl_get_entry_by_name(struct in_addr *, struct key *, |
645 | const char *, struct afs_cache_vlocation *, | 652 | const char *, struct afs_cache_vlocation *, |
646 | const struct afs_wait_mode *); | 653 | const struct afs_wait_mode *); |
@@ -664,12 +671,6 @@ extern void afs_vlocation_purge(void); | |||
664 | /* | 671 | /* |
665 | * vnode.c | 672 | * vnode.c |
666 | */ | 673 | */ |
667 | #ifdef AFS_CACHING_SUPPORT | ||
668 | extern struct cachefs_index_def afs_vnode_cache_index_def; | ||
669 | #endif | ||
670 | |||
671 | extern struct afs_timer_ops afs_vnode_cb_timed_out_ops; | ||
672 | |||
673 | static inline struct afs_vnode *AFS_FS_I(struct inode *inode) | 674 | static inline struct afs_vnode *AFS_FS_I(struct inode *inode) |
674 | { | 675 | { |
675 | return container_of(inode, struct afs_vnode, vfs_inode); | 676 | return container_of(inode, struct afs_vnode, vfs_inode); |
@@ -711,10 +712,6 @@ extern int afs_vnode_release_lock(struct afs_vnode *, struct key *); | |||
711 | /* | 712 | /* |
712 | * volume.c | 713 | * volume.c |
713 | */ | 714 | */ |
714 | #ifdef AFS_CACHING_SUPPORT | ||
715 | extern struct cachefs_index_def afs_volume_cache_index_def; | ||
716 | #endif | ||
717 | |||
718 | #define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0) | 715 | #define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0) |
719 | 716 | ||
720 | extern void afs_put_volume(struct afs_volume *); | 717 | extern void afs_put_volume(struct afs_volume *); |
diff --git a/fs/afs/main.c b/fs/afs/main.c index 2d3e5d4fb9f7..66d54d348c55 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* AFS client file system | 1 | /* AFS client file system |
2 | * | 2 | * |
3 | * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright (C) 2002,5 Red Hat, Inc. All Rights Reserved. |
4 | * Written by David Howells (dhowells@redhat.com) | 4 | * Written by David Howells (dhowells@redhat.com) |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
@@ -29,18 +29,6 @@ static char *rootcell; | |||
29 | module_param(rootcell, charp, 0); | 29 | module_param(rootcell, charp, 0); |
30 | MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list"); | 30 | MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list"); |
31 | 31 | ||
32 | #ifdef AFS_CACHING_SUPPORT | ||
33 | static struct cachefs_netfs_operations afs_cache_ops = { | ||
34 | .get_page_cookie = afs_cache_get_page_cookie, | ||
35 | }; | ||
36 | |||
37 | struct cachefs_netfs afs_cache_netfs = { | ||
38 | .name = "afs", | ||
39 | .version = 0, | ||
40 | .ops = &afs_cache_ops, | ||
41 | }; | ||
42 | #endif | ||
43 | |||
44 | struct afs_uuid afs_uuid; | 32 | struct afs_uuid afs_uuid; |
45 | 33 | ||
46 | /* | 34 | /* |
@@ -104,10 +92,9 @@ static int __init afs_init(void) | |||
104 | if (ret < 0) | 92 | if (ret < 0) |
105 | return ret; | 93 | return ret; |
106 | 94 | ||
107 | #ifdef AFS_CACHING_SUPPORT | 95 | #ifdef CONFIG_AFS_FSCACHE |
108 | /* we want to be able to cache */ | 96 | /* we want to be able to cache */ |
109 | ret = cachefs_register_netfs(&afs_cache_netfs, | 97 | ret = fscache_register_netfs(&afs_cache_netfs); |
110 | &afs_cache_cell_index_def); | ||
111 | if (ret < 0) | 98 | if (ret < 0) |
112 | goto error_cache; | 99 | goto error_cache; |
113 | #endif | 100 | #endif |
@@ -142,8 +129,8 @@ error_fs: | |||
142 | error_open_socket: | 129 | error_open_socket: |
143 | error_vl_update_init: | 130 | error_vl_update_init: |
144 | error_cell_init: | 131 | error_cell_init: |
145 | #ifdef AFS_CACHING_SUPPORT | 132 | #ifdef CONFIG_AFS_FSCACHE |
146 | cachefs_unregister_netfs(&afs_cache_netfs); | 133 | fscache_unregister_netfs(&afs_cache_netfs); |
147 | error_cache: | 134 | error_cache: |
148 | #endif | 135 | #endif |
149 | afs_callback_update_kill(); | 136 | afs_callback_update_kill(); |
@@ -175,8 +162,8 @@ static void __exit afs_exit(void) | |||
175 | afs_vlocation_purge(); | 162 | afs_vlocation_purge(); |
176 | flush_scheduled_work(); | 163 | flush_scheduled_work(); |
177 | afs_cell_purge(); | 164 | afs_cell_purge(); |
178 | #ifdef AFS_CACHING_SUPPORT | 165 | #ifdef CONFIG_AFS_FSCACHE |
179 | cachefs_unregister_netfs(&afs_cache_netfs); | 166 | fscache_unregister_netfs(&afs_cache_netfs); |
180 | #endif | 167 | #endif |
181 | afs_proc_cleanup(); | 168 | afs_proc_cleanup(); |
182 | rcu_barrier(); | 169 | rcu_barrier(); |
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 78db4953a800..2b9e2d03a390 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c | |||
@@ -173,9 +173,9 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) | |||
173 | if (PageError(page)) | 173 | if (PageError(page)) |
174 | goto error; | 174 | goto error; |
175 | 175 | ||
176 | buf = kmap(page); | 176 | buf = kmap_atomic(page, KM_USER0); |
177 | memcpy(devname, buf, size); | 177 | memcpy(devname, buf, size); |
178 | kunmap(page); | 178 | kunmap_atomic(buf, KM_USER0); |
179 | page_cache_release(page); | 179 | page_cache_release(page); |
180 | page = NULL; | 180 | page = NULL; |
181 | 181 | ||
diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c index 849fc3160cb5..ec2a7431e458 100644 --- a/fs/afs/vlocation.c +++ b/fs/afs/vlocation.c | |||
@@ -281,9 +281,8 @@ static void afs_vlocation_apply_update(struct afs_vlocation *vl, | |||
281 | 281 | ||
282 | vl->vldb = *vldb; | 282 | vl->vldb = *vldb; |
283 | 283 | ||
284 | #ifdef AFS_CACHING_SUPPORT | 284 | #ifdef CONFIG_AFS_FSCACHE |
285 | /* update volume entry in local cache */ | 285 | fscache_update_cookie(vl->cache); |
286 | cachefs_update_cookie(vl->cache); | ||
287 | #endif | 286 | #endif |
288 | } | 287 | } |
289 | 288 | ||
@@ -304,11 +303,9 @@ static int afs_vlocation_fill_in_record(struct afs_vlocation *vl, | |||
304 | memset(&vldb, 0, sizeof(vldb)); | 303 | memset(&vldb, 0, sizeof(vldb)); |
305 | 304 | ||
306 | /* see if we have an in-cache copy (will set vl->valid if there is) */ | 305 | /* see if we have an in-cache copy (will set vl->valid if there is) */ |
307 | #ifdef AFS_CACHING_SUPPORT | 306 | #ifdef CONFIG_AFS_FSCACHE |
308 | cachefs_acquire_cookie(cell->cache, | 307 | vl->cache = fscache_acquire_cookie(vl->cell->cache, |
309 | &afs_volume_cache_index_def, | 308 | &afs_vlocation_cache_index_def, vl); |
310 | vlocation, | ||
311 | &vl->cache); | ||
312 | #endif | 309 | #endif |
313 | 310 | ||
314 | if (vl->valid) { | 311 | if (vl->valid) { |
@@ -420,6 +417,11 @@ fill_in_record: | |||
420 | spin_unlock(&vl->lock); | 417 | spin_unlock(&vl->lock); |
421 | wake_up(&vl->waitq); | 418 | wake_up(&vl->waitq); |
422 | 419 | ||
420 | /* update volume entry in local cache */ | ||
421 | #ifdef CONFIG_AFS_FSCACHE | ||
422 | fscache_update_cookie(vl->cache); | ||
423 | #endif | ||
424 | |||
423 | /* schedule for regular updates */ | 425 | /* schedule for regular updates */ |
424 | afs_vlocation_queue_for_updates(vl); | 426 | afs_vlocation_queue_for_updates(vl); |
425 | goto success; | 427 | goto success; |
@@ -465,7 +467,7 @@ found_in_memory: | |||
465 | spin_unlock(&vl->lock); | 467 | spin_unlock(&vl->lock); |
466 | 468 | ||
467 | success: | 469 | success: |
468 | _leave(" = %p",vl); | 470 | _leave(" = %p", vl); |
469 | return vl; | 471 | return vl; |
470 | 472 | ||
471 | error_abandon: | 473 | error_abandon: |
@@ -523,10 +525,9 @@ static void afs_vlocation_destroy(struct afs_vlocation *vl) | |||
523 | { | 525 | { |
524 | _enter("%p", vl); | 526 | _enter("%p", vl); |
525 | 527 | ||
526 | #ifdef AFS_CACHING_SUPPORT | 528 | #ifdef CONFIG_AFS_FSCACHE |
527 | cachefs_relinquish_cookie(vl->cache, 0); | 529 | fscache_relinquish_cookie(vl->cache, 0); |
528 | #endif | 530 | #endif |
529 | |||
530 | afs_put_cell(vl->cell); | 531 | afs_put_cell(vl->cell); |
531 | kfree(vl); | 532 | kfree(vl); |
532 | } | 533 | } |
diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 8bab0e3437f9..a353e69e2391 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c | |||
@@ -124,13 +124,11 @@ struct afs_volume *afs_volume_lookup(struct afs_mount_params *params) | |||
124 | } | 124 | } |
125 | 125 | ||
126 | /* attach the cache and volume location */ | 126 | /* attach the cache and volume location */ |
127 | #ifdef AFS_CACHING_SUPPORT | 127 | #ifdef CONFIG_AFS_FSCACHE |
128 | cachefs_acquire_cookie(vlocation->cache, | 128 | volume->cache = fscache_acquire_cookie(vlocation->cache, |
129 | &afs_vnode_cache_index_def, | 129 | &afs_volume_cache_index_def, |
130 | volume, | 130 | volume); |
131 | &volume->cache); | ||
132 | #endif | 131 | #endif |
133 | |||
134 | afs_get_vlocation(vlocation); | 132 | afs_get_vlocation(vlocation); |
135 | volume->vlocation = vlocation; | 133 | volume->vlocation = vlocation; |
136 | 134 | ||
@@ -194,8 +192,8 @@ void afs_put_volume(struct afs_volume *volume) | |||
194 | up_write(&vlocation->cell->vl_sem); | 192 | up_write(&vlocation->cell->vl_sem); |
195 | 193 | ||
196 | /* finish cleaning up the volume */ | 194 | /* finish cleaning up the volume */ |
197 | #ifdef AFS_CACHING_SUPPORT | 195 | #ifdef CONFIG_AFS_FSCACHE |
198 | cachefs_relinquish_cookie(volume->cache, 0); | 196 | fscache_relinquish_cookie(volume->cache, 0); |
199 | #endif | 197 | #endif |
200 | afs_put_vlocation(vlocation); | 198 | afs_put_vlocation(vlocation); |
201 | 199 | ||
diff --git a/fs/afs/write.c b/fs/afs/write.c index 3fb36d433621..c2e7a7ff0080 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c | |||
@@ -780,3 +780,24 @@ int afs_fsync(struct file *file, struct dentry *dentry, int datasync) | |||
780 | _leave(" = %d", ret); | 780 | _leave(" = %d", ret); |
781 | return ret; | 781 | return ret; |
782 | } | 782 | } |
783 | |||
784 | /* | ||
785 | * notification that a previously read-only page is about to become writable | ||
786 | * - if it returns an error, the caller will deliver a bus error signal | ||
787 | */ | ||
788 | int afs_page_mkwrite(struct vm_area_struct *vma, struct page *page) | ||
789 | { | ||
790 | struct afs_vnode *vnode = AFS_FS_I(vma->vm_file->f_mapping->host); | ||
791 | |||
792 | _enter("{{%x:%u}},{%lx}", | ||
793 | vnode->fid.vid, vnode->fid.vnode, page->index); | ||
794 | |||
795 | /* wait for the page to be written to the cache before we allow it to | ||
796 | * be modified */ | ||
797 | #ifdef CONFIG_AFS_FSCACHE | ||
798 | fscache_wait_on_page_write(vnode->cache, page); | ||
799 | #endif | ||
800 | |||
801 | _leave(" = 0"); | ||
802 | return 0; | ||
803 | } | ||