diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 13:16:30 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 16:07:36 -0400 |
commit | 951a143b3fcf15cfa9d38250b7462f821db241db (patch) | |
tree | 6e4ea13c5f48cc3e1ac1c8649dd0f9f20c502e20 | |
parent | 08e9eac42edab63bce14b5c8419771f3c92aa3f4 (diff) |
[PATCH] NFS: Fix the file size revalidation
Instead of looking at whether or not the file is open for writes before
we accept to update the length using the server value, we should rather
be looking at whether or not we are currently caching any writes.
Failure to do so means in particular that we're not updating the file
length correctly after obtaining a POSIX or BSD lock.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/direct.c | 2 | ||||
-rw-r--r-- | fs/nfs/inode.c | 69 | ||||
-rw-r--r-- | fs/nfs/write.c | 4 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 1 |
4 files changed, 21 insertions, 55 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 68df803f27ca..d6a30c844de3 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c | |||
@@ -517,7 +517,7 @@ retry: | |||
517 | result = tot_bytes; | 517 | result = tot_bytes; |
518 | 518 | ||
519 | out: | 519 | out: |
520 | nfs_end_data_update_defer(inode); | 520 | nfs_end_data_update(inode); |
521 | nfs_writedata_free(wdata); | 521 | nfs_writedata_free(wdata); |
522 | return result; | 522 | return result; |
523 | 523 | ||
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 6300e05e9463..b2d16758ced8 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -1148,27 +1148,6 @@ void nfs_end_data_update(struct inode *inode) | |||
1148 | } | 1148 | } |
1149 | 1149 | ||
1150 | /** | 1150 | /** |
1151 | * nfs_end_data_update_defer | ||
1152 | * @inode - pointer to inode | ||
1153 | * Declare end of the operations that will update file data | ||
1154 | * This will defer marking the inode as needing revalidation | ||
1155 | * unless there are no other pending updates. | ||
1156 | */ | ||
1157 | void nfs_end_data_update_defer(struct inode *inode) | ||
1158 | { | ||
1159 | struct nfs_inode *nfsi = NFS_I(inode); | ||
1160 | |||
1161 | if (atomic_dec_and_test(&nfsi->data_updates)) { | ||
1162 | /* Mark the attribute cache for revalidation */ | ||
1163 | nfsi->flags |= NFS_INO_INVALID_ATTR; | ||
1164 | /* Directories and symlinks: invalidate page cache too */ | ||
1165 | if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) | ||
1166 | nfsi->flags |= NFS_INO_INVALID_DATA; | ||
1167 | nfsi->cache_change_attribute ++; | ||
1168 | } | ||
1169 | } | ||
1170 | |||
1171 | /** | ||
1172 | * nfs_refresh_inode - verify consistency of the inode attribute cache | 1151 | * nfs_refresh_inode - verify consistency of the inode attribute cache |
1173 | * @inode - pointer to inode | 1152 | * @inode - pointer to inode |
1174 | * @fattr - updated attributes | 1153 | * @fattr - updated attributes |
@@ -1222,8 +1201,8 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) | |||
1222 | if (!timespec_equal(&inode->i_mtime, &fattr->mtime) | 1201 | if (!timespec_equal(&inode->i_mtime, &fattr->mtime) |
1223 | || cur_size != new_isize) | 1202 | || cur_size != new_isize) |
1224 | nfsi->flags |= NFS_INO_INVALID_ATTR; | 1203 | nfsi->flags |= NFS_INO_INVALID_ATTR; |
1225 | } else if (S_ISREG(inode->i_mode) && new_isize > cur_size) | 1204 | } else if (new_isize != cur_size && nfsi->npages == 0) |
1226 | nfsi->flags |= NFS_INO_INVALID_ATTR; | 1205 | nfsi->flags |= NFS_INO_INVALID_ATTR; |
1227 | 1206 | ||
1228 | /* Have any file permissions changed? */ | 1207 | /* Have any file permissions changed? */ |
1229 | if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) | 1208 | if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) |
@@ -1257,10 +1236,8 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) | |||
1257 | static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier) | 1236 | static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier) |
1258 | { | 1237 | { |
1259 | struct nfs_inode *nfsi = NFS_I(inode); | 1238 | struct nfs_inode *nfsi = NFS_I(inode); |
1260 | __u64 new_size; | 1239 | loff_t cur_isize, new_isize; |
1261 | loff_t new_isize; | ||
1262 | unsigned int invalid = 0; | 1240 | unsigned int invalid = 0; |
1263 | loff_t cur_isize; | ||
1264 | int data_unstable; | 1241 | int data_unstable; |
1265 | 1242 | ||
1266 | dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n", | 1243 | dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n", |
@@ -1293,49 +1270,39 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign | |||
1293 | /* Are we racing with known updates of the metadata on the server? */ | 1270 | /* Are we racing with known updates of the metadata on the server? */ |
1294 | data_unstable = ! nfs_verify_change_attribute(inode, verifier); | 1271 | data_unstable = ! nfs_verify_change_attribute(inode, verifier); |
1295 | 1272 | ||
1296 | /* Check if the file size agrees */ | 1273 | /* Check if our cached file size is stale */ |
1297 | new_size = fattr->size; | ||
1298 | new_isize = nfs_size_to_loff_t(fattr->size); | 1274 | new_isize = nfs_size_to_loff_t(fattr->size); |
1299 | cur_isize = i_size_read(inode); | 1275 | cur_isize = i_size_read(inode); |
1300 | if (cur_isize != new_size) { | 1276 | if (new_isize != cur_isize) { |
1301 | #ifdef NFS_DEBUG_VERBOSE | 1277 | /* Do we perhaps have any outstanding writes? */ |
1302 | printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); | 1278 | if (nfsi->npages == 0) { |
1303 | #endif | 1279 | /* No, but did we race with nfs_end_data_update()? */ |
1304 | /* | 1280 | if (verifier == nfsi->cache_change_attribute) { |
1305 | * If we have pending writebacks, things can get | ||
1306 | * messy. | ||
1307 | */ | ||
1308 | if (S_ISREG(inode->i_mode) && data_unstable) { | ||
1309 | if (new_isize > cur_isize) { | ||
1310 | inode->i_size = new_isize; | 1281 | inode->i_size = new_isize; |
1311 | invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; | 1282 | invalid |= NFS_INO_INVALID_DATA; |
1312 | } | 1283 | } |
1313 | } else { | 1284 | invalid |= NFS_INO_INVALID_ATTR; |
1285 | } else if (new_isize > cur_isize) { | ||
1314 | inode->i_size = new_isize; | 1286 | inode->i_size = new_isize; |
1315 | invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; | 1287 | invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; |
1316 | } | 1288 | } |
1289 | dprintk("NFS: isize change on server for file %s/%ld\n", | ||
1290 | inode->i_sb->s_id, inode->i_ino); | ||
1317 | } | 1291 | } |
1318 | 1292 | ||
1319 | /* | 1293 | /* Check if the mtime agrees */ |
1320 | * Note: we don't check inode->i_mtime since pipes etc. | ||
1321 | * can change this value in VFS without requiring a | ||
1322 | * cache revalidation. | ||
1323 | */ | ||
1324 | if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { | 1294 | if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { |
1325 | memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); | 1295 | memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); |
1326 | #ifdef NFS_DEBUG_VERBOSE | 1296 | dprintk("NFS: mtime change on server for file %s/%ld\n", |
1327 | printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); | 1297 | inode->i_sb->s_id, inode->i_ino); |
1328 | #endif | ||
1329 | if (!data_unstable) | 1298 | if (!data_unstable) |
1330 | invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; | 1299 | invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; |
1331 | } | 1300 | } |
1332 | 1301 | ||
1333 | if ((fattr->valid & NFS_ATTR_FATTR_V4) | 1302 | if ((fattr->valid & NFS_ATTR_FATTR_V4) |
1334 | && nfsi->change_attr != fattr->change_attr) { | 1303 | && nfsi->change_attr != fattr->change_attr) { |
1335 | #ifdef NFS_DEBUG_VERBOSE | 1304 | dprintk("NFS: change_attr change on server for file %s/%ld\n", |
1336 | printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n", | ||
1337 | inode->i_sb->s_id, inode->i_ino); | 1305 | inode->i_sb->s_id, inode->i_ino); |
1338 | #endif | ||
1339 | nfsi->change_attr = fattr->change_attr; | 1306 | nfsi->change_attr = fattr->change_attr; |
1340 | if (!data_unstable) | 1307 | if (!data_unstable) |
1341 | invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; | 1308 | invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 6f7a4af3bc46..c574d551f029 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -220,7 +220,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode, | |||
220 | ClearPageError(page); | 220 | ClearPageError(page); |
221 | 221 | ||
222 | io_error: | 222 | io_error: |
223 | nfs_end_data_update_defer(inode); | 223 | nfs_end_data_update(inode); |
224 | nfs_writedata_free(wdata); | 224 | nfs_writedata_free(wdata); |
225 | return written ? written : result; | 225 | return written ? written : result; |
226 | } | 226 | } |
@@ -401,7 +401,7 @@ static void nfs_inode_remove_request(struct nfs_page *req) | |||
401 | nfsi->npages--; | 401 | nfsi->npages--; |
402 | if (!nfsi->npages) { | 402 | if (!nfsi->npages) { |
403 | spin_unlock(&nfsi->req_lock); | 403 | spin_unlock(&nfsi->req_lock); |
404 | nfs_end_data_update_defer(inode); | 404 | nfs_end_data_update(inode); |
405 | iput(inode); | 405 | iput(inode); |
406 | } else | 406 | } else |
407 | spin_unlock(&nfsi->req_lock); | 407 | spin_unlock(&nfsi->req_lock); |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index c90313bfa435..211266c56ce5 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -294,7 +294,6 @@ extern void nfs_begin_attr_update(struct inode *); | |||
294 | extern void nfs_end_attr_update(struct inode *); | 294 | extern void nfs_end_attr_update(struct inode *); |
295 | extern void nfs_begin_data_update(struct inode *); | 295 | extern void nfs_begin_data_update(struct inode *); |
296 | extern void nfs_end_data_update(struct inode *); | 296 | extern void nfs_end_data_update(struct inode *); |
297 | extern void nfs_end_data_update_defer(struct inode *); | ||
298 | extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred); | 297 | extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred); |
299 | extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); | 298 | extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); |
300 | extern void put_nfs_open_context(struct nfs_open_context *ctx); | 299 | extern void put_nfs_open_context(struct nfs_open_context *ctx); |