diff options
| author | J. Bruce Fields <bfields@citi.umich.edu> | 2009-04-16 17:33:25 -0400 |
|---|---|---|
| committer | J. Bruce Fields <bfields@citi.umich.edu> | 2009-04-29 11:35:49 -0400 |
| commit | c654b8a9cba6002aad1c01919e4928a79a4a6dcf (patch) | |
| tree | fee411bb42ec86e72a9e843529551e559c1f2749 | |
| parent | 3352d2c2d0540955a7bbb3421a28330af7f9d79c (diff) | |
nfsd: support ext4 i_version
ext4 supports a real NFSv4 change attribute, which is bumped whenever
the ctime would be updated, including times when two updates arrive
within a jiffy of each other. (Note that although ext4 has space for
nanosecond-precision ctime, the real resolution is lower: it actually
uses jiffies as the time-source.) This ensures clients will invalidate
their caches when they need to.
There is some fear that keeping the i_version up-to-date could have
performance drawbacks, so for now it's turned on only by a mount option.
We hope to do something better eventually.
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Cc: Theodore Tso <tytso@mit.edu>
| -rw-r--r-- | fs/nfsd/nfs3xdr.c | 1 | ||||
| -rw-r--r-- | fs/nfsd/nfs4xdr.c | 63 | ||||
| -rw-r--r-- | include/linux/nfsd/nfsfh.h | 7 | ||||
| -rw-r--r-- | include/linux/nfsd/xdr4.h | 17 |
4 files changed, 63 insertions, 25 deletions
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 17d0dd997204..01d4ec1c88e0 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c | |||
| @@ -272,6 +272,7 @@ void fill_post_wcc(struct svc_fh *fhp) | |||
| 272 | 272 | ||
| 273 | err = vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry, | 273 | err = vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry, |
| 274 | &fhp->fh_post_attr); | 274 | &fhp->fh_post_attr); |
| 275 | fhp->fh_post_change = fhp->fh_dentry->d_inode->i_version; | ||
| 275 | if (err) | 276 | if (err) |
| 276 | fhp->fh_post_saved = 0; | 277 | fhp->fh_post_saved = 0; |
| 277 | else | 278 | else |
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 4a71fcd3f036..12d36a7361cd 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
| @@ -1490,13 +1490,41 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) | |||
| 1490 | memcpy(p, ptr, nbytes); \ | 1490 | memcpy(p, ptr, nbytes); \ |
| 1491 | p += XDR_QUADLEN(nbytes); \ | 1491 | p += XDR_QUADLEN(nbytes); \ |
| 1492 | }} while (0) | 1492 | }} while (0) |
| 1493 | #define WRITECINFO(c) do { \ | 1493 | |
| 1494 | *p++ = htonl(c.atomic); \ | 1494 | static void write32(__be32 **p, u32 n) |
| 1495 | *p++ = htonl(c.before_ctime_sec); \ | 1495 | { |
| 1496 | *p++ = htonl(c.before_ctime_nsec); \ | 1496 | *(*p)++ = n; |
| 1497 | *p++ = htonl(c.after_ctime_sec); \ | 1497 | } |
| 1498 | *p++ = htonl(c.after_ctime_nsec); \ | 1498 | |
| 1499 | } while (0) | 1499 | static void write64(__be32 **p, u64 n) |
| 1500 | { | ||
| 1501 | write32(p, (u32)(n >> 32)); | ||
| 1502 | write32(p, (u32)n); | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | static void write_change(__be32 **p, struct kstat *stat, struct inode *inode) | ||
| 1506 | { | ||
| 1507 | if (IS_I_VERSION(inode)) { | ||
| 1508 | write64(p, inode->i_version); | ||
| 1509 | } else { | ||
| 1510 | write32(p, stat->ctime.tv_sec); | ||
| 1511 | write32(p, stat->ctime.tv_nsec); | ||
| 1512 | } | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | static void write_cinfo(__be32 **p, struct nfsd4_change_info *c) | ||
| 1516 | { | ||
| 1517 | write32(p, c->atomic); | ||
| 1518 | if (c->change_supported) { | ||
| 1519 | write64(p, c->before_change); | ||
| 1520 | write64(p, c->after_change); | ||
| 1521 | } else { | ||
| 1522 | write32(p, c->before_ctime_sec); | ||
| 1523 | write32(p, c->before_ctime_nsec); | ||
| 1524 | write32(p, c->after_ctime_sec); | ||
| 1525 | write32(p, c->after_ctime_nsec); | ||
| 1526 | } | ||
| 1527 | } | ||
| 1500 | 1528 | ||
| 1501 | #define RESERVE_SPACE(nbytes) do { \ | 1529 | #define RESERVE_SPACE(nbytes) do { \ |
| 1502 | p = resp->p; \ | 1530 | p = resp->p; \ |
| @@ -1849,16 +1877,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, | |||
| 1849 | WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME); | 1877 | WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME); |
| 1850 | } | 1878 | } |
| 1851 | if (bmval0 & FATTR4_WORD0_CHANGE) { | 1879 | if (bmval0 & FATTR4_WORD0_CHANGE) { |
| 1852 | /* | ||
| 1853 | * Note: This _must_ be consistent with the scheme for writing | ||
| 1854 | * change_info, so any changes made here must be reflected there | ||
| 1855 | * as well. (See xdr4.h:set_change_info() and the WRITECINFO() | ||
| 1856 | * macro above.) | ||
| 1857 | */ | ||
| 1858 | if ((buflen -= 8) < 0) | 1880 | if ((buflen -= 8) < 0) |
| 1859 | goto out_resource; | 1881 | goto out_resource; |
| 1860 | WRITE32(stat.ctime.tv_sec); | 1882 | write_change(&p, &stat, dentry->d_inode); |
| 1861 | WRITE32(stat.ctime.tv_nsec); | ||
| 1862 | } | 1883 | } |
| 1863 | if (bmval0 & FATTR4_WORD0_SIZE) { | 1884 | if (bmval0 & FATTR4_WORD0_SIZE) { |
| 1864 | if ((buflen -= 8) < 0) | 1885 | if ((buflen -= 8) < 0) |
| @@ -2364,7 +2385,7 @@ nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ | |||
| 2364 | 2385 | ||
| 2365 | if (!nfserr) { | 2386 | if (!nfserr) { |
| 2366 | RESERVE_SPACE(32); | 2387 | RESERVE_SPACE(32); |
| 2367 | WRITECINFO(create->cr_cinfo); | 2388 | write_cinfo(&p, &create->cr_cinfo); |
| 2368 | WRITE32(2); | 2389 | WRITE32(2); |
| 2369 | WRITE32(create->cr_bmval[0]); | 2390 | WRITE32(create->cr_bmval[0]); |
| 2370 | WRITE32(create->cr_bmval[1]); | 2391 | WRITE32(create->cr_bmval[1]); |
| @@ -2475,7 +2496,7 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_li | |||
| 2475 | 2496 | ||
| 2476 | if (!nfserr) { | 2497 | if (!nfserr) { |
| 2477 | RESERVE_SPACE(20); | 2498 | RESERVE_SPACE(20); |
| 2478 | WRITECINFO(link->li_cinfo); | 2499 | write_cinfo(&p, &link->li_cinfo); |
| 2479 | ADJUST_ARGS(); | 2500 | ADJUST_ARGS(); |
| 2480 | } | 2501 | } |
| 2481 | return nfserr; | 2502 | return nfserr; |
| @@ -2493,7 +2514,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op | |||
| 2493 | 2514 | ||
| 2494 | nfsd4_encode_stateid(resp, &open->op_stateid); | 2515 | nfsd4_encode_stateid(resp, &open->op_stateid); |
| 2495 | RESERVE_SPACE(40); | 2516 | RESERVE_SPACE(40); |
| 2496 | WRITECINFO(open->op_cinfo); | 2517 | write_cinfo(&p, &open->op_cinfo); |
| 2497 | WRITE32(open->op_rflags); | 2518 | WRITE32(open->op_rflags); |
| 2498 | WRITE32(2); | 2519 | WRITE32(2); |
| 2499 | WRITE32(open->op_bmval[0]); | 2520 | WRITE32(open->op_bmval[0]); |
| @@ -2771,7 +2792,7 @@ nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ | |||
| 2771 | 2792 | ||
| 2772 | if (!nfserr) { | 2793 | if (!nfserr) { |
| 2773 | RESERVE_SPACE(20); | 2794 | RESERVE_SPACE(20); |
| 2774 | WRITECINFO(remove->rm_cinfo); | 2795 | write_cinfo(&p, &remove->rm_cinfo); |
| 2775 | ADJUST_ARGS(); | 2796 | ADJUST_ARGS(); |
| 2776 | } | 2797 | } |
| 2777 | return nfserr; | 2798 | return nfserr; |
| @@ -2784,8 +2805,8 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ | |||
| 2784 | 2805 | ||
| 2785 | if (!nfserr) { | 2806 | if (!nfserr) { |
| 2786 | RESERVE_SPACE(40); | 2807 | RESERVE_SPACE(40); |
| 2787 | WRITECINFO(rename->rn_sinfo); | 2808 | write_cinfo(&p, &rename->rn_sinfo); |
| 2788 | WRITECINFO(rename->rn_tinfo); | 2809 | write_cinfo(&p, &rename->rn_tinfo); |
| 2789 | ADJUST_ARGS(); | 2810 | ADJUST_ARGS(); |
| 2790 | } | 2811 | } |
| 2791 | return nfserr; | 2812 | return nfserr; |
diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h index afa19016c4a8..8f641c908450 100644 --- a/include/linux/nfsd/nfsfh.h +++ b/include/linux/nfsd/nfsfh.h | |||
| @@ -151,9 +151,15 @@ typedef struct svc_fh { | |||
| 151 | __u64 fh_pre_size; /* size before operation */ | 151 | __u64 fh_pre_size; /* size before operation */ |
| 152 | struct timespec fh_pre_mtime; /* mtime before oper */ | 152 | struct timespec fh_pre_mtime; /* mtime before oper */ |
| 153 | struct timespec fh_pre_ctime; /* ctime before oper */ | 153 | struct timespec fh_pre_ctime; /* ctime before oper */ |
| 154 | /* | ||
| 155 | * pre-op nfsv4 change attr: note must check IS_I_VERSION(inode) | ||
| 156 | * to find out if it is valid. | ||
| 157 | */ | ||
| 158 | u64 fh_pre_change; | ||
| 154 | 159 | ||
| 155 | /* Post-op attributes saved in fh_unlock */ | 160 | /* Post-op attributes saved in fh_unlock */ |
| 156 | struct kstat fh_post_attr; /* full attrs after operation */ | 161 | struct kstat fh_post_attr; /* full attrs after operation */ |
| 162 | u64 fh_post_change; /* nfsv4 change; see above */ | ||
| 157 | #endif /* CONFIG_NFSD_V3 */ | 163 | #endif /* CONFIG_NFSD_V3 */ |
| 158 | 164 | ||
| 159 | } svc_fh; | 165 | } svc_fh; |
| @@ -298,6 +304,7 @@ fill_pre_wcc(struct svc_fh *fhp) | |||
| 298 | fhp->fh_pre_mtime = inode->i_mtime; | 304 | fhp->fh_pre_mtime = inode->i_mtime; |
| 299 | fhp->fh_pre_ctime = inode->i_ctime; | 305 | fhp->fh_pre_ctime = inode->i_ctime; |
| 300 | fhp->fh_pre_size = inode->i_size; | 306 | fhp->fh_pre_size = inode->i_size; |
| 307 | fhp->fh_pre_change = inode->i_version; | ||
| 301 | fhp->fh_pre_saved = 1; | 308 | fhp->fh_pre_saved = 1; |
| 302 | } | 309 | } |
| 303 | } | 310 | } |
diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index f80d6013fdc3..d0f050f01eca 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h | |||
| @@ -64,10 +64,13 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs) | |||
| 64 | 64 | ||
| 65 | struct nfsd4_change_info { | 65 | struct nfsd4_change_info { |
| 66 | u32 atomic; | 66 | u32 atomic; |
| 67 | bool change_supported; | ||
| 67 | u32 before_ctime_sec; | 68 | u32 before_ctime_sec; |
| 68 | u32 before_ctime_nsec; | 69 | u32 before_ctime_nsec; |
| 70 | u64 before_change; | ||
| 69 | u32 after_ctime_sec; | 71 | u32 after_ctime_sec; |
| 70 | u32 after_ctime_nsec; | 72 | u32 after_ctime_nsec; |
| 73 | u64 after_change; | ||
| 71 | }; | 74 | }; |
| 72 | 75 | ||
| 73 | struct nfsd4_access { | 76 | struct nfsd4_access { |
| @@ -503,10 +506,16 @@ set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp) | |||
| 503 | { | 506 | { |
| 504 | BUG_ON(!fhp->fh_pre_saved || !fhp->fh_post_saved); | 507 | BUG_ON(!fhp->fh_pre_saved || !fhp->fh_post_saved); |
| 505 | cinfo->atomic = 1; | 508 | cinfo->atomic = 1; |
| 506 | cinfo->before_ctime_sec = fhp->fh_pre_ctime.tv_sec; | 509 | cinfo->change_supported = IS_I_VERSION(fhp->fh_dentry->d_inode); |
| 507 | cinfo->before_ctime_nsec = fhp->fh_pre_ctime.tv_nsec; | 510 | if (cinfo->change_supported) { |
| 508 | cinfo->after_ctime_sec = fhp->fh_post_attr.ctime.tv_sec; | 511 | cinfo->before_change = fhp->fh_pre_change; |
| 509 | cinfo->after_ctime_nsec = fhp->fh_post_attr.ctime.tv_nsec; | 512 | cinfo->after_change = fhp->fh_post_change; |
| 513 | } else { | ||
| 514 | cinfo->before_ctime_sec = fhp->fh_pre_ctime.tv_sec; | ||
| 515 | cinfo->before_ctime_nsec = fhp->fh_pre_ctime.tv_nsec; | ||
| 516 | cinfo->after_ctime_sec = fhp->fh_post_attr.ctime.tv_sec; | ||
| 517 | cinfo->after_ctime_nsec = fhp->fh_post_attr.ctime.tv_nsec; | ||
| 518 | } | ||
| 510 | } | 519 | } |
| 511 | 520 | ||
| 512 | int nfs4svc_encode_voidres(struct svc_rqst *, __be32 *, void *); | 521 | int nfs4svc_encode_voidres(struct svc_rqst *, __be32 *, void *); |
