From 832023bffb4b493f230be901f681020caf3ed1f8 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Mon, 8 Aug 2011 17:38:08 +0200 Subject: [PATCH 001/103] nfsd4: Remove check for a 32-bit cookie in nfsd4_readdir() Fan Yong noticed setting FMODE_32bithash wouldn't work with nfsd v4, as nfsd4_readdir() checks for 32 bit cookies. However, according to RFC 3530 cookies have a 64 bit type and cookies are also defined as u64 in 'struct nfsd4_readdir'. So remove the test for >32-bit values. Cc: stable@kernel.org Signed-off-by: Bernd Schubert Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index e80777666618..9bf0a6625187 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -691,7 +691,7 @@ nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, readdir->rd_bmval[1] &= nfsd_suppattrs1(cstate->minorversion); readdir->rd_bmval[2] &= nfsd_suppattrs2(cstate->minorversion); - if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) || + if ((cookie == 1) || (cookie == 2) || (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE))) return nfserr_bad_cookie; From 576163005de286bbd418fcb99cfd0971523a0c6d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 10 Aug 2011 19:16:22 -0400 Subject: [PATCH 002/103] nfsd4: fix seqid_mutating_error The set of errors here does *not* agree with the set of errors specified in the rfc! While we're there, turn this macros into a function, for the usual reasons, and move it to the one place where it's actually used. Cc: stable@kernel.org Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 12 ++++++++++++ fs/nfsd/state.h | 6 ------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index c8bf405d19de..f81099605256 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1623,6 +1623,18 @@ static void write_cinfo(__be32 **p, struct nfsd4_change_info *c) \ save = resp->p; +static bool seqid_mutating_err(__be32 err) +{ + /* rfc 3530 section 8.1.5: */ + return err != nfserr_stale_clientid && + err != nfserr_stale_stateid && + err != nfserr_bad_stateid && + err != nfserr_bad_seqid && + err != nfserr_bad_xdr && + err != nfserr_resource && + err != nfserr_nofilehandle; +} + /* * Routine for encoding the result of a "seqid-mutating" NFSv4 operation. This * is where sequence id's are incremented, and the replay cache is filled. diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 4eefaf1b42e8..5cfebe504056 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -447,12 +447,6 @@ struct nfs4_stateid { #define WR_STATE 0x00000020 #define CLOSE_STATE 0x00000040 -#define seqid_mutating_err(err) \ - (((err) != nfserr_stale_clientid) && \ - ((err) != nfserr_bad_seqid) && \ - ((err) != nfserr_stale_stateid) && \ - ((err) != nfserr_bad_stateid)) - struct nfsd4_compound_state; extern __be32 nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, From aadab6c6f4da38d639394de740602f146c88da0c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 15 Aug 2011 16:55:02 -0400 Subject: [PATCH 003/103] nfsd4: return nfserr_symlink on v4 OPEN of non-regular file Without this, an attempt to open a device special file without first stat'ing it will fail. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 9bf0a6625187..d784ceb81a62 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -168,6 +168,24 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs return status; } +static __be32 nfsd_check_obj_isreg(struct svc_fh *fh) +{ + umode_t mode = fh->fh_dentry->d_inode->i_mode; + + if (S_ISREG(mode)) + return nfs_ok; + if (S_ISDIR(mode)) + return nfserr_isdir; + /* + * Using err_symlink as our catch-all case may look odd; but + * there's no other obvious error for this case in 4.0, and we + * happen to know that it will cause the linux v4 client to do + * the right thing on attempts to open something other than a + * regular file. + */ + return nfserr_symlink; +} + static __be32 do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) { @@ -216,6 +234,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o status = nfsd_lookup(rqstp, current_fh, open->op_fname.data, open->op_fname.len, &resfh); fh_unlock(current_fh); + if (status) + goto out; + status = nfsd_check_obj_isreg(&resfh); } if (status) goto out; From ab83fa4b49a54e6199b076b7d8c1808144e80f0d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 26 Jul 2011 20:10:51 -0400 Subject: [PATCH 004/103] locks: minor lease cleanup Use a helper function, to simplify upcoming changes. Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/locks.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 703f545097de..c528522862b1 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -133,6 +133,11 @@ #define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK) #define IS_LEASE(fl) (fl->fl_flags & FL_LEASE) +static bool lease_breaking(struct file_lock *fl) +{ + return fl->fl_type & F_INPROGRESS; +} + int leases_enable = 1; int lease_break_time = 45; @@ -1141,7 +1146,7 @@ static void time_out_leases(struct inode *inode) struct file_lock *fl; before = &inode->i_flock; - while ((fl = *before) && IS_LEASE(fl) && (fl->fl_type & F_INPROGRESS)) { + while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) { if ((fl->fl_break_time == 0) || time_before(jiffies, fl->fl_break_time)) { before = &fl->fl_next; @@ -1189,7 +1194,7 @@ int __break_lease(struct inode *inode, unsigned int mode) if (want_write) { /* If we want write access, we have to revoke any lease. */ future = F_UNLCK | F_INPROGRESS; - } else if (flock->fl_type & F_INPROGRESS) { + } else if (lease_breaking(flock)) { /* If the lease is already being broken, we just leave it */ future = flock->fl_type; } else if (flock->fl_type & F_WRLCK) { @@ -1246,7 +1251,7 @@ restart: /* Wait for the next lease that has not been broken yet */ for (flock = inode->i_flock; flock && IS_LEASE(flock); flock = flock->fl_next) { - if (flock->fl_type & F_INPROGRESS) + if (lease_breaking(flock)) goto restart; } error = 0; @@ -2126,7 +2131,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, } } else if (IS_LEASE(fl)) { seq_printf(f, "LEASE "); - if (fl->fl_type & F_INPROGRESS) + if (lease_breaking(fl)) seq_printf(f, "BREAKING "); else if (fl->fl_file) seq_printf(f, "ACTIVE "); @@ -2142,7 +2147,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE "); } else { seq_printf(f, "%s ", - (fl->fl_type & F_INPROGRESS) + (lease_breaking(fl)) ? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ " : (fl->fl_type & F_WRLCK) ? "WRITE" : "READ "); } From 710b7216964d6455cf1b215c43b03a1a79008c7d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 26 Jul 2011 16:28:29 -0400 Subject: [PATCH 005/103] locks: move F_INPROGRESS from fl_type to fl_flags field F_INPROGRESS isn't exposed to userspace. To me it makes more sense in fl_flags.... Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- arch/alpha/include/asm/fcntl.h | 2 -- fs/locks.c | 14 ++++++++------ include/asm-generic/fcntl.h | 5 ----- include/linux/fs.h | 3 ++- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/arch/alpha/include/asm/fcntl.h b/arch/alpha/include/asm/fcntl.h index 1b71ca70c9f6..6d9e805f18a7 100644 --- a/arch/alpha/include/asm/fcntl.h +++ b/arch/alpha/include/asm/fcntl.h @@ -51,8 +51,6 @@ #define F_EXLCK 16 /* or 3 */ #define F_SHLCK 32 /* or 4 */ -#define F_INPROGRESS 64 - #include #endif diff --git a/fs/locks.c b/fs/locks.c index c528522862b1..c4215418bca3 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -135,7 +135,7 @@ static bool lease_breaking(struct file_lock *fl) { - return fl->fl_type & F_INPROGRESS; + return fl->fl_flags & FL_INPROGRESS; } int leases_enable = 1; @@ -1132,6 +1132,7 @@ int lease_modify(struct file_lock **before, int arg) if (error) return error; + fl->fl_flags &= ~FL_INPROGRESS; locks_wake_up_blocks(fl); if (arg == F_UNLCK) locks_delete_lock(before); @@ -1152,7 +1153,7 @@ static void time_out_leases(struct inode *inode) before = &fl->fl_next; continue; } - lease_modify(before, fl->fl_type & ~F_INPROGRESS); + lease_modify(before, fl->fl_type); if (fl == *before) /* lease_modify may have freed fl */ before = &fl->fl_next; } @@ -1193,13 +1194,13 @@ int __break_lease(struct inode *inode, unsigned int mode) if (want_write) { /* If we want write access, we have to revoke any lease. */ - future = F_UNLCK | F_INPROGRESS; + future = F_UNLCK; } else if (lease_breaking(flock)) { /* If the lease is already being broken, we just leave it */ future = flock->fl_type; } else if (flock->fl_type & F_WRLCK) { /* Downgrade the exclusive lease to a read-only lease. */ - future = F_RDLCK | F_INPROGRESS; + future = F_RDLCK; } else { /* the existing lease was read-only, so we can read too. */ goto out; @@ -1221,6 +1222,7 @@ int __break_lease(struct inode *inode, unsigned int mode) for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { if (fl->fl_type != future) { fl->fl_type = future; + fl->fl_flags |= FL_INPROGRESS; fl->fl_break_time = break_time; /* lease must have lmops break callback */ fl->fl_lmops->lm_break(fl); @@ -1319,7 +1321,7 @@ int fcntl_getlease(struct file *filp) for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl); fl = fl->fl_next) { if (fl->fl_file == filp) { - type = fl->fl_type & ~F_INPROGRESS; + type = fl->fl_type; break; } } @@ -1384,7 +1386,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) before = &fl->fl_next) { if (fl->fl_file == filp) my_before = before; - else if (fl->fl_type == (F_INPROGRESS | F_UNLCK)) + else if ((fl->fl_type == F_UNLCK) && lease_breaking(fl)) /* * Someone is in the process of opening this * file for writing so we may not take an diff --git a/include/asm-generic/fcntl.h b/include/asm-generic/fcntl.h index 84793c7025e2..9e5b0356e2bb 100644 --- a/include/asm-generic/fcntl.h +++ b/include/asm-generic/fcntl.h @@ -145,11 +145,6 @@ struct f_owner_ex { #define F_SHLCK 8 /* or 4 */ #endif -/* for leases */ -#ifndef F_INPROGRESS -#define F_INPROGRESS 16 -#endif - /* operations for bsd flock(), also used by the kernel implementation */ #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 178cdb4f1d4a..327fdd4de85f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1065,6 +1065,7 @@ static inline int file_check_writeable(struct file *filp) #define FL_LEASE 32 /* lease held on this file */ #define FL_CLOSE 64 /* unlock on close */ #define FL_SLEEP 128 /* A blocking lock */ +#define FL_INPROGRESS 256 /* Lease is being broken */ /* * Special return value from posix_lock_file() and vfs_lock_file() for @@ -1111,7 +1112,7 @@ struct file_lock { struct list_head fl_link; /* doubly linked list of all locks */ struct list_head fl_block; /* circular list of blocked processes */ fl_owner_t fl_owner; - unsigned char fl_flags; + unsigned int fl_flags; unsigned char fl_type; unsigned int fl_pid; struct pid *fl_nspid; From 778fc546f749c588aa2f6cd50215d2715c374252 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 26 Jul 2011 18:25:49 -0400 Subject: [PATCH 006/103] locks: fix tracking of inprogress lease breaks We currently use a bit in fl_flags to record whether a lease is being broken, and set fl_type to the type (RDLCK or UNLCK) that it will eventually have. This means that once the lease break starts, we forget what the lease's type *used* to be. Breaking a read lease will then result in blocking read opens, even though there's no conflict--because the lease type is now F_UNLCK and we can no longer tell whether it was previously a read or write lease. So, instead keep fl_type as the original type (the type which we enforce), and keep track of whether we're unlocking or merely downgrading by replacing the single FL_INPROGRESS flag by FL_UNLOCK_PENDING and FL_DOWNGRADE_PENDING flags. To get this right we also need to track separate downgrade and break times, to handle the case where a write-leased file gets conflicting opens first for read, then later for write. (I first considered just eliminating the downgrade behavior completely--nfsv4 doesn't need it, and nobody as far as I can tell actually uses it currently--but Jeremy Allison tells me that Windows oplocks do behave this way, so Samba will probably use this some day.) Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/locks.c | 87 +++++++++++++++++++++++++++++----------------- include/linux/fs.h | 7 ++-- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index c4215418bca3..c525aa4de234 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -135,7 +135,16 @@ static bool lease_breaking(struct file_lock *fl) { - return fl->fl_flags & FL_INPROGRESS; + return fl->fl_flags & (FL_UNLOCK_PENDING | FL_DOWNGRADE_PENDING); +} + +static int target_leasetype(struct file_lock *fl) +{ + if (fl->fl_flags & FL_UNLOCK_PENDING) + return F_UNLCK; + if (fl->fl_flags & FL_DOWNGRADE_PENDING) + return F_RDLCK; + return fl->fl_type; } int leases_enable = 1; @@ -1124,6 +1133,17 @@ int locks_mandatory_area(int read_write, struct inode *inode, EXPORT_SYMBOL(locks_mandatory_area); +static void lease_clear_pending(struct file_lock *fl, int arg) +{ + switch (arg) { + case F_UNLCK: + fl->fl_flags &= ~FL_UNLOCK_PENDING; + /* fall through: */ + case F_RDLCK: + fl->fl_flags &= ~FL_DOWNGRADE_PENDING; + } +} + /* We already had a lease on this file; just change its type */ int lease_modify(struct file_lock **before, int arg) { @@ -1132,7 +1152,7 @@ int lease_modify(struct file_lock **before, int arg) if (error) return error; - fl->fl_flags &= ~FL_INPROGRESS; + lease_clear_pending(fl, arg); locks_wake_up_blocks(fl); if (arg == F_UNLCK) locks_delete_lock(before); @@ -1141,6 +1161,14 @@ int lease_modify(struct file_lock **before, int arg) EXPORT_SYMBOL(lease_modify); +static bool past_time(unsigned long then) +{ + if (!then) + /* 0 is a special value meaning "this never expires": */ + return false; + return time_after(jiffies, then); +} + static void time_out_leases(struct inode *inode) { struct file_lock **before; @@ -1148,12 +1176,10 @@ static void time_out_leases(struct inode *inode) before = &inode->i_flock; while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) { - if ((fl->fl_break_time == 0) - || time_before(jiffies, fl->fl_break_time)) { - before = &fl->fl_next; - continue; - } - lease_modify(before, fl->fl_type); + if (past_time(fl->fl_downgrade_time)) + lease_modify(before, F_RDLCK); + if (past_time(fl->fl_break_time)) + lease_modify(before, F_UNLCK); if (fl == *before) /* lease_modify may have freed fl */ before = &fl->fl_next; } @@ -1171,7 +1197,7 @@ static void time_out_leases(struct inode *inode) */ int __break_lease(struct inode *inode, unsigned int mode) { - int error = 0, future; + int error = 0; struct file_lock *new_fl, *flock; struct file_lock *fl; unsigned long break_time; @@ -1188,24 +1214,13 @@ int __break_lease(struct inode *inode, unsigned int mode) if ((flock == NULL) || !IS_LEASE(flock)) goto out; + if (!locks_conflict(flock, new_fl)) + goto out; + for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) if (fl->fl_owner == current->files) i_have_this_lease = 1; - if (want_write) { - /* If we want write access, we have to revoke any lease. */ - future = F_UNLCK; - } else if (lease_breaking(flock)) { - /* If the lease is already being broken, we just leave it */ - future = flock->fl_type; - } else if (flock->fl_type & F_WRLCK) { - /* Downgrade the exclusive lease to a read-only lease. */ - future = F_RDLCK; - } else { - /* the existing lease was read-only, so we can read too. */ - goto out; - } - if (IS_ERR(new_fl) && !i_have_this_lease && ((mode & O_NONBLOCK) == 0)) { error = PTR_ERR(new_fl); @@ -1220,13 +1235,18 @@ int __break_lease(struct inode *inode, unsigned int mode) } for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { - if (fl->fl_type != future) { - fl->fl_type = future; - fl->fl_flags |= FL_INPROGRESS; + if (want_write) { + if (fl->fl_flags & FL_UNLOCK_PENDING) + continue; + fl->fl_flags |= FL_UNLOCK_PENDING; fl->fl_break_time = break_time; - /* lease must have lmops break callback */ - fl->fl_lmops->lm_break(fl); + } else { + if (lease_breaking(flock)) + continue; + fl->fl_flags |= FL_DOWNGRADE_PENDING; + fl->fl_downgrade_time = break_time; } + fl->fl_lmops->lm_break(fl); } if (i_have_this_lease || (mode & O_NONBLOCK)) { @@ -1250,10 +1270,13 @@ restart: if (error >= 0) { if (error == 0) time_out_leases(inode); - /* Wait for the next lease that has not been broken yet */ + /* + * Wait for the next conflicting lease that has not been + * broken yet + */ for (flock = inode->i_flock; flock && IS_LEASE(flock); flock = flock->fl_next) { - if (lease_breaking(flock)) + if (locks_conflict(new_fl, flock)) goto restart; } error = 0; @@ -1321,7 +1344,7 @@ int fcntl_getlease(struct file *filp) for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl); fl = fl->fl_next) { if (fl->fl_file == filp) { - type = fl->fl_type; + type = target_leasetype(fl); break; } } @@ -1386,7 +1409,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) before = &fl->fl_next) { if (fl->fl_file == filp) my_before = before; - else if ((fl->fl_type == F_UNLCK) && lease_breaking(fl)) + else if (fl->fl_flags & FL_UNLOCK_PENDING) /* * Someone is in the process of opening this * file for writing so we may not take an diff --git a/include/linux/fs.h b/include/linux/fs.h index 327fdd4de85f..76460edf1648 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1065,7 +1065,8 @@ static inline int file_check_writeable(struct file *filp) #define FL_LEASE 32 /* lease held on this file */ #define FL_CLOSE 64 /* unlock on close */ #define FL_SLEEP 128 /* A blocking lock */ -#define FL_INPROGRESS 256 /* Lease is being broken */ +#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */ +#define FL_UNLOCK_PENDING 512 /* Lease is being broken */ /* * Special return value from posix_lock_file() and vfs_lock_file() for @@ -1122,7 +1123,9 @@ struct file_lock { loff_t fl_end; struct fasync_struct * fl_fasync; /* for lease break notifications */ - unsigned long fl_break_time; /* for nonblocking lease breaks */ + /* for lease breaks: */ + unsigned long fl_break_time; + unsigned long fl_downgrade_time; const struct file_lock_operations *fl_ops; /* Callbacks for filesystems */ const struct lock_manager_operations *fl_lmops; /* Callbacks for lockmanagers */ From c1f24ef4ed46f58ea5e524a2364c93b6847fb164 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 19 Aug 2011 10:59:49 -0400 Subject: [PATCH 007/103] locks: setlease cleanup There's an incorrect comment here. Also clean up the logic: the "rdlease" and "wrlease" locals are confusingly named, and don't really add anything since we can make a decision as soon as we hit one of these cases. Signed-off-by: J. Bruce Fields --- fs/locks.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index c525aa4de234..9b8408eb6946 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1368,7 +1368,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) struct file_lock *fl, **before, **my_before = NULL, *lease; struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; - int error, rdlease_count = 0, wrlease_count = 0; + int error; lease = *flp; @@ -1404,27 +1404,28 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) * then the file is not open by anyone (including us) * except for this filp. */ + error = -EAGAIN; for (before = &inode->i_flock; ((fl = *before) != NULL) && IS_LEASE(fl); before = &fl->fl_next) { - if (fl->fl_file == filp) + if (fl->fl_file == filp) { my_before = before; - else if (fl->fl_flags & FL_UNLOCK_PENDING) - /* - * Someone is in the process of opening this - * file for writing so we may not take an - * exclusive lease on it. - */ - wrlease_count++; - else - rdlease_count++; + continue; + } + /* + * No exclusive leases if someone else has a lease on + * this file: + */ + if (arg == F_WRLCK) + goto out; + /* + * Modifying our existing lease is OK, but no getting a + * new lease if someone else is opening for write: + */ + if (fl->fl_flags & FL_UNLOCK_PENDING) + goto out; } - error = -EAGAIN; - if ((arg == F_RDLCK && (wrlease_count > 0)) || - (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) - goto out; - if (my_before != NULL) { error = lease->fl_lmops->lm_change(my_before, arg); if (!error) From 11fd165c68b73434ca1273e21f21db5eecc90926 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 28 Jul 2011 20:04:09 +0200 Subject: [PATCH 008/103] sunrpc: use better NUMA affinities Use NUMA aware allocations to reduce latencies and increase throughput. sunrpc kthreads can use kthread_create_on_node() if pool_mode is "percpu" or "pernode", and svc_prepare_thread()/svc_init_buffer() can also take into account NUMA node affinity for memory allocations. Signed-off-by: Eric Dumazet CC: "J. Bruce Fields" CC: Neil Brown CC: David Miller Reviewed-by: Greg Banks [bfields@redhat.com: fix up caller nfs41_callback_up] Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 2 +- fs/nfs/callback.c | 4 ++-- include/linux/sunrpc/svc.h | 2 +- net/sunrpc/svc.c | 33 ++++++++++++++++++++++++--------- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index abfff9d7979d..c061b9aa7ddb 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -282,7 +282,7 @@ int lockd_up(void) /* * Create the kernel thread and wait for it to start. */ - nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); + nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); if (IS_ERR(nlmsvc_rqst)) { error = PTR_ERR(nlmsvc_rqst); nlmsvc_rqst = NULL; diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index e3d294269058..516f3375e067 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -125,7 +125,7 @@ nfs4_callback_up(struct svc_serv *serv) else goto out_err; - return svc_prepare_thread(serv, &serv->sv_pools[0]); + return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); out_err: if (ret == 0) @@ -199,7 +199,7 @@ nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) INIT_LIST_HEAD(&serv->sv_cb_list); spin_lock_init(&serv->sv_cb_lock); init_waitqueue_head(&serv->sv_cb_waitq); - rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); + rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); if (IS_ERR(rqstp)) { svc_xprt_put(serv->sv_bc_xprt); serv->sv_bc_xprt = NULL; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 223588a976a0..a78a51e93373 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -404,7 +404,7 @@ struct svc_procedure { struct svc_serv *svc_create(struct svc_program *, unsigned int, void (*shutdown)(struct svc_serv *)); struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, - struct svc_pool *pool); + struct svc_pool *pool, int node); void svc_exit_thread(struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, void (*shutdown)(struct svc_serv *), diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 6a69a1131fb7..30d70abb4e2c 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -295,6 +295,18 @@ svc_pool_map_put(void) } +static int svc_pool_map_get_node(unsigned int pidx) +{ + const struct svc_pool_map *m = &svc_pool_map; + + if (m->count) { + if (m->mode == SVC_POOL_PERCPU) + return cpu_to_node(m->pool_to[pidx]); + if (m->mode == SVC_POOL_PERNODE) + return m->pool_to[pidx]; + } + return NUMA_NO_NODE; +} /* * Set the given thread's cpus_allowed mask so that it * will only run on cpus in the given pool. @@ -499,7 +511,7 @@ EXPORT_SYMBOL_GPL(svc_destroy); * We allocate pages and place them in rq_argpages. */ static int -svc_init_buffer(struct svc_rqst *rqstp, unsigned int size) +svc_init_buffer(struct svc_rqst *rqstp, unsigned int size, int node) { unsigned int pages, arghi; @@ -513,7 +525,7 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size) arghi = 0; BUG_ON(pages > RPCSVC_MAXPAGES); while (pages) { - struct page *p = alloc_page(GFP_KERNEL); + struct page *p = alloc_pages_node(node, GFP_KERNEL, 0); if (!p) break; rqstp->rq_pages[arghi++] = p; @@ -536,11 +548,11 @@ svc_release_buffer(struct svc_rqst *rqstp) } struct svc_rqst * -svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool) +svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node) { struct svc_rqst *rqstp; - rqstp = kzalloc(sizeof(*rqstp), GFP_KERNEL); + rqstp = kzalloc_node(sizeof(*rqstp), GFP_KERNEL, node); if (!rqstp) goto out_enomem; @@ -554,15 +566,15 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool) rqstp->rq_server = serv; rqstp->rq_pool = pool; - rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL); + rqstp->rq_argp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node); if (!rqstp->rq_argp) goto out_thread; - rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL); + rqstp->rq_resp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node); if (!rqstp->rq_resp) goto out_thread; - if (!svc_init_buffer(rqstp, serv->sv_max_mesg)) + if (!svc_init_buffer(rqstp, serv->sv_max_mesg, node)) goto out_thread; return rqstp; @@ -647,6 +659,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) struct svc_pool *chosen_pool; int error = 0; unsigned int state = serv->sv_nrthreads-1; + int node; if (pool == NULL) { /* The -1 assumes caller has done a svc_get() */ @@ -662,14 +675,16 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) nrservs--; chosen_pool = choose_pool(serv, pool, &state); - rqstp = svc_prepare_thread(serv, chosen_pool); + node = svc_pool_map_get_node(chosen_pool->sp_id); + rqstp = svc_prepare_thread(serv, chosen_pool, node); if (IS_ERR(rqstp)) { error = PTR_ERR(rqstp); break; } __module_get(serv->sv_module); - task = kthread_create(serv->sv_function, rqstp, serv->sv_name); + task = kthread_create_on_node(serv->sv_function, rqstp, + node, serv->sv_name); if (IS_ERR(task)) { error = PTR_ERR(task); module_put(serv->sv_module); From 3d2544b1e4909b6dffa0d140273628913e255e45 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 15 Aug 2011 11:49:30 -0400 Subject: [PATCH 009/103] nfsd4: clean up S_IS -> NF4 file type mapping A slightly unconventional approach to make the code more compact I could live with, but let's give the poor reader *some* chance. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index f81099605256..51ec1f274501 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1772,12 +1772,19 @@ static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp, return 0; } -static u32 nfs4_ftypes[16] = { - NF4BAD, NF4FIFO, NF4CHR, NF4BAD, - NF4DIR, NF4BAD, NF4BLK, NF4BAD, - NF4REG, NF4BAD, NF4LNK, NF4BAD, - NF4SOCK, NF4BAD, NF4LNK, NF4BAD, -}; +static u32 nfs4_file_type(umode_t mode) +{ + switch (mode & S_IFMT) { + case S_IFIFO: return NF4FIFO; + case S_IFCHR: return NF4CHR; + case S_IFDIR: return NF4DIR; + case S_IFBLK: return NF4BLK; + case S_IFLNK: return NF4LNK; + case S_IFREG: return NF4REG; + case S_IFSOCK: return NF4SOCK; + default: return NF4BAD; + }; +} static __be32 nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, @@ -1966,7 +1973,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_TYPE) { if ((buflen -= 4) < 0) goto out_resource; - dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12]; + dummy = nfs4_file_type(stat.mode); if (dummy == NF4BAD) goto out_serverfault; WRITE32(dummy); From 7d818a7b8fc8d26c24ee44ed1c5dece69455a7b6 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 15 Aug 2011 16:59:55 -0400 Subject: [PATCH 010/103] nfsd: open-code special directory-hardlink check We allow the fh_verify caller to specify that any object *except* those of a given type is allowed, by passing a negative type. But only one caller actually uses it. Open-code that check in the one caller. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsfh.c | 9 --------- fs/nfsd/vfs.c | 6 ++++-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 90c6aa6d5e0f..8cd49b9bf085 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -61,7 +61,6 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry) static inline __be32 nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type) { - /* Type can be negative when creating hardlinks - not to a dir */ if (type > 0 && (mode & S_IFMT) != type) { if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) return nfserr_symlink; @@ -72,14 +71,6 @@ nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type) else return nfserr_inval; } - if (type < 0 && (mode & S_IFMT) == -type) { - if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) - return nfserr_symlink; - else if (type == -S_IFDIR) - return nfserr_isdir; - else - return nfserr_notdir; - } return 0; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index fd0acca5370a..0c92a17d1770 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1632,10 +1632,12 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; - err = fh_verify(rqstp, tfhp, -S_IFDIR, NFSD_MAY_NOP); + err = fh_verify(rqstp, tfhp, 0, NFSD_MAY_NOP); if (err) goto out; - + err = nfserr_isdir; + if (S_ISDIR(tfhp->fh_dentry->d_inode->i_mode)) + goto out; err = nfserr_perm; if (!len) goto out; From e10f9e1413576f58c18b89f6ae212a37a88b24e7 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 15 Aug 2011 17:04:19 -0400 Subject: [PATCH 011/103] nfsd: clean up nfsd_mode_check() Add some more comments, simplify logic, do & S_IFMT just once, name "type" more helpfully. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsfh.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 8cd49b9bf085..c763de5c1157 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -59,19 +59,25 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry) * the write call). */ static inline __be32 -nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type) +nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int requested) { - if (type > 0 && (mode & S_IFMT) != type) { - if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) - return nfserr_symlink; - else if (type == S_IFDIR) - return nfserr_notdir; - else if ((mode & S_IFMT) == S_IFDIR) - return nfserr_isdir; - else - return nfserr_inval; - } - return 0; + mode &= S_IFMT; + + if (requested == 0) /* the caller doesn't care */ + return nfs_ok; + if (mode == requested) + return nfs_ok; + /* + * v4 has an error more specific than err_notdir which we should + * return in preference to err_notdir: + */ + if (rqstp->rq_vers == 4 && mode == S_IFLNK) + return nfserr_symlink; + if (requested == S_IFDIR) + return nfserr_notdir; + if (mode == S_IFDIR) + return nfserr_isdir; + return nfserr_inval; } static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, From e281d8100995133dc65e00b1dec8f84b91b6e8c3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 15 Aug 2011 16:57:07 -0400 Subject: [PATCH 012/103] nfsd4: fix incorrect comment in nfsd4_set_nfs4_acl Zero means "I don't care what kind of file this is". And that's probably what we want--acls are also settable at least on directories, and if the filesystem doesn't want them on other objects, leave it to it to complain. Signed-off-by: J. Bruce Fields --- fs/nfsd/vfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 0c92a17d1770..4c22870293e6 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -502,7 +502,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned int flags = 0; /* Get inode */ - error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR); + error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR); if (error) return error; From 75c096f753b273b59f1b9a0745e9e4b5d911a312 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 15 Aug 2011 18:39:32 -0400 Subject: [PATCH 013/103] nfsd4: it's OK to return nfserr_symlink The nfsd4 code has a bunch of special exceptions for error returns which map nfserr_symlink to other errors. In fact, the spec makes it clear that nfserr_symlink is to be preferred over less specific errors where possible. The patch that introduced it back in 2.6.4 is "kNFSd: correct symlink related error returns.", which claims that these special exceptions are represent an NFSv4 break from v2/v3 tradition--when in fact the symlink error was introduced with v4. I suspect what happened was pynfs tests were written that were overly faithful to the (known-incomplete) rfc3530 error return lists, and then code was fixed up mindlessly to make the tests pass. Delete these unnecessary exceptions. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 15 +-------------- fs/nfsd/nfs4state.c | 6 +----- fs/nfsd/nfs4xdr.c | 4 ---- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index d784ceb81a62..479ffb185df9 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -488,17 +488,12 @@ static __be32 nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_commit *commit) { - __be32 status; - u32 *p = (u32 *)commit->co_verf.data; *p++ = nfssvc_boot.tv_sec; *p++ = nfssvc_boot.tv_usec; - status = nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, + return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, commit->co_count); - if (status == nfserr_symlink) - status = nfserr_inval; - return status; } static __be32 @@ -513,8 +508,6 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_CREATE); - if (status == nfserr_symlink) - status = nfserr_notdir; if (status) return status; @@ -740,8 +733,6 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return nfserr_grace; status = nfsd_unlink(rqstp, &cstate->current_fh, 0, remove->rm_name, remove->rm_namelen); - if (status == nfserr_symlink) - return nfserr_notdir; if (!status) { fh_unlock(&cstate->current_fh); set_change_info(&remove->rm_cinfo, &cstate->current_fh); @@ -772,8 +763,6 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, (S_ISDIR(cstate->save_fh.fh_dentry->d_inode->i_mode) && S_ISDIR(cstate->current_fh.fh_dentry->d_inode->i_mode))) status = nfserr_exist; - else if (status == nfserr_symlink) - status = nfserr_notdir; if (!status) { set_change_info(&rename->rn_sinfo, &cstate->current_fh); @@ -913,8 +902,6 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, write->wr_bytes_written = cnt; - if (status == nfserr_symlink) - status = nfserr_inval; return status; } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3787ec117400..aa0a36e3b09e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4179,12 +4179,8 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid)) goto out; - if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) { - dprintk("NFSD: nfsd4_lockt: fh_verify() failed!\n"); - if (status == nfserr_symlink) - status = nfserr_inval; + if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) goto out; - } inode = cstate->current_fh.fh_dentry->d_inode; locks_init_lock(&file_lock); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 51ec1f274501..78c792fb59a8 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2778,8 +2778,6 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen, &maxcount); - if (nfserr == nfserr_symlink) - nfserr = nfserr_inval; if (nfserr) return nfserr; eof = (read->rd_offset + maxcount >= @@ -2905,8 +2903,6 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 readdir->common.err == nfserr_toosmall && readdir->buffer == page) nfserr = nfserr_toosmall; - if (nfserr == nfserr_symlink) - nfserr = nfserr_notdir; if (nfserr) goto err_no_verf; From 8cfb79134027e96bc11067da00d7ca73e58e69ce Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 8 Aug 2011 07:09:03 -0400 Subject: [PATCH 014/103] nfsd: remove unused defines At least one of these is actually wrong anyway. Signed-off-by: J. Bruce Fields --- include/linux/nfsd/const.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/include/linux/nfsd/const.h b/include/linux/nfsd/const.h index 323f8cfa060a..feb3764617a4 100644 --- a/include/linux/nfsd/const.h +++ b/include/linux/nfsd/const.h @@ -14,11 +14,6 @@ #include #include -/* - * Maximum protocol version supported by knfsd - */ -#define NFSSVC_MAXVERS 3 - /* * Maximum blocksizes supported by daemon under various circumstances. */ @@ -42,14 +37,6 @@ */ #define NFSD_BUFSIZE ((RPC_MAX_HEADER_WITH_AUTH+26)*XDR_UNIT + NFSSVC_MAXBLKSIZE) -#ifdef CONFIG_NFSD_V4 -# define NFSSVC_XDRSIZE NFS4_SVC_XDRSIZE -#elif defined(CONFIG_NFSD_V3) -# define NFSSVC_XDRSIZE NFS3_SVC_XDRSIZE -#else -# define NFSSVC_XDRSIZE NFS2_SVC_XDRSIZE -#endif - #endif /* __KERNEL__ */ #endif /* _LINUX_NFSD_CONST_H */ From c10bd39d800d42adef55ed9016f802677cd0ab5f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 19 Aug 2011 11:38:52 -0400 Subject: [PATCH 015/103] Remove include/linux/nfsd/const.h Userspace shouldn't have a use for these constants. Nothing here is used outside fs/nfsd. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsd.h | 26 +++++++++++++++++++++++ include/linux/nfsd/Kbuild | 1 - include/linux/nfsd/const.h | 42 -------------------------------------- include/linux/nfsd/nfsfh.h | 7 +++++-- 4 files changed, 31 insertions(+), 45 deletions(-) delete mode 100644 include/linux/nfsd/const.h diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 7ecfa2420307..8da03e16ab35 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -11,13 +11,39 @@ #include #include +#include +#include +#include +#include +#include + #include #include #include + /* * nfsd version */ #define NFSD_SUPPORTED_MINOR_VERSION 1 +/* + * Maximum blocksizes supported by daemon under various circumstances. + */ +#define NFSSVC_MAXBLKSIZE RPCSVC_MAXPAYLOAD +/* NFSv2 is limited by the protocol specification, see RFC 1094 */ +#define NFSSVC_MAXBLKSIZE_V2 (8*1024) + + +/* + * Largest number of bytes we need to allocate for an NFS + * call or reply. Used to control buffer sizes. We use + * the length of v3 WRITE, READDIR and READDIR replies + * which are an RPC header, up to 26 XDR units of reply + * data, and some page data. + * + * Note that accuracy here doesn't matter too much as the + * size is rounded up to a page size when allocating space. + */ +#define NFSD_BUFSIZE ((RPC_MAX_HEADER_WITH_AUTH+26)*XDR_UNIT + NFSSVC_MAXBLKSIZE) struct readdir_cd { __be32 err; /* 0, nfserr, or nfserr_eof */ diff --git a/include/linux/nfsd/Kbuild b/include/linux/nfsd/Kbuild index 55d1467de3c1..0e528606d46f 100644 --- a/include/linux/nfsd/Kbuild +++ b/include/linux/nfsd/Kbuild @@ -1,4 +1,3 @@ -header-y += const.h header-y += debug.h header-y += export.h header-y += nfsfh.h diff --git a/include/linux/nfsd/const.h b/include/linux/nfsd/const.h deleted file mode 100644 index feb3764617a4..000000000000 --- a/include/linux/nfsd/const.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * include/linux/nfsd/const.h - * - * Various constants related to NFS. - * - * Copyright (C) 1995-1997 Olaf Kirch - */ - -#ifndef _LINUX_NFSD_CONST_H -#define _LINUX_NFSD_CONST_H - -#include -#include -#include -#include - -/* - * Maximum blocksizes supported by daemon under various circumstances. - */ -#define NFSSVC_MAXBLKSIZE RPCSVC_MAXPAYLOAD -/* NFSv2 is limited by the protocol specification, see RFC 1094 */ -#define NFSSVC_MAXBLKSIZE_V2 (8*1024) - -#ifdef __KERNEL__ - -#include - -/* - * Largest number of bytes we need to allocate for an NFS - * call or reply. Used to control buffer sizes. We use - * the length of v3 WRITE, READDIR and READDIR replies - * which are an RPC header, up to 26 XDR units of reply - * data, and some page data. - * - * Note that accuracy here doesn't matter too much as the - * size is rounded up to a page size when allocating space. - */ -#define NFSD_BUFSIZE ((RPC_MAX_HEADER_WITH_AUTH+26)*XDR_UNIT + NFSSVC_MAXBLKSIZE) - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_NFSD_CONST_H */ diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h index f76d80ccec10..ce4743a26015 100644 --- a/include/linux/nfsd/nfsfh.h +++ b/include/linux/nfsd/nfsfh.h @@ -14,11 +14,14 @@ #ifndef _LINUX_NFSD_FH_H #define _LINUX_NFSD_FH_H -# include +#include +#include +#include +#include +#include #ifdef __KERNEL__ # include #endif -#include /* * This is the old "dentry style" Linux NFSv2 file handle. From a043226bc140a2c1dde162246d68a67e5043e6b2 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 25 Aug 2011 10:48:39 -0400 Subject: [PATCH 016/103] nfsd4: permit read opens of executable-only files A client that wants to execute a file must be able to read it. Read opens over nfs are therefore implicitly allowed for executable files even when those files are not readable. NFSv2/v3 get this right by using a passed-in NFSD_MAY_OWNER_OVERRIDE on read requests, but NFSv4 has gotten this wrong ever since dc730e173785e29b297aa605786c94adaffe2544 "nfsd4: fix owner-override on open", when we realized that the file owner shouldn't override permissions on non-reclaim NFSv4 opens. So we can't use NFSD_MAY_OWNER_OVERRIDE to tell nfsd_permission to allow reads of executable files. So, do the same thing we do whenever we encounter another weird NFS permission nit: define yet another NFSD_MAY_* flag. The industry's future standardization on 128-bit processors will be motivated primarily by the need for integers with enough bits for all the NFSD_MAY_* flags. Reported-by: Leonardo Borda Cc: stable@kernel.org Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 2 ++ fs/nfsd/vfs.c | 3 ++- fs/nfsd/vfs.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 479ffb185df9..b5530984db91 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -156,6 +156,8 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs !(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) return nfserr_inval; + accmode |= NFSD_MAY_READ_IF_EXEC; + if (open->op_share_access & NFS4_SHARE_ACCESS_READ) accmode |= NFSD_MAY_READ; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 4c22870293e6..75c35fa46155 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -2116,7 +2116,8 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, /* Allow read access to binaries even when mode 111 */ if (err == -EACCES && S_ISREG(inode->i_mode) && - acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE)) + (acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE) || + acc == (NFSD_MAY_READ | NFSD_MAY_READ_IF_EXEC))) err = inode_permission(inode, MAY_EXEC); return err? nfserrno(err) : 0; diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index e0bbac04d1dd..a22e40e27861 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -25,6 +25,7 @@ #define NFSD_MAY_BYPASS_GSS_ON_ROOT 256 #define NFSD_MAY_NOT_BREAK_LEASE 512 #define NFSD_MAY_BYPASS_GSS 1024 +#define NFSD_MAY_READ_IF_EXEC 2048 #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) From 8e82fa8fdcd1271d45bf6a5195801c706e190d69 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 25 Aug 2011 14:23:39 -0400 Subject: [PATCH 017/103] nfsd: prettify NFSD_MAY_* flag definitions Acked-by: Jim Rees Signed-off-by: J. Bruce Fields --- fs/nfsd/vfs.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index a22e40e27861..503f3bf11abd 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -10,22 +10,22 @@ /* * Flags for nfsd_permission */ -#define NFSD_MAY_NOP 0 -#define NFSD_MAY_EXEC 1 /* == MAY_EXEC */ -#define NFSD_MAY_WRITE 2 /* == MAY_WRITE */ -#define NFSD_MAY_READ 4 /* == MAY_READ */ -#define NFSD_MAY_SATTR 8 -#define NFSD_MAY_TRUNC 16 -#define NFSD_MAY_LOCK 32 -#define NFSD_MAY_MASK 63 +#define NFSD_MAY_NOP 0 +#define NFSD_MAY_EXEC 0x001 /* == MAY_EXEC */ +#define NFSD_MAY_WRITE 0x002 /* == MAY_WRITE */ +#define NFSD_MAY_READ 0x004 /* == MAY_READ */ +#define NFSD_MAY_SATTR 0x008 +#define NFSD_MAY_TRUNC 0x010 +#define NFSD_MAY_LOCK 0x020 +#define NFSD_MAY_MASK 0x03f /* extra hints to permission and open routines: */ -#define NFSD_MAY_OWNER_OVERRIDE 64 -#define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ -#define NFSD_MAY_BYPASS_GSS_ON_ROOT 256 -#define NFSD_MAY_NOT_BREAK_LEASE 512 -#define NFSD_MAY_BYPASS_GSS 1024 -#define NFSD_MAY_READ_IF_EXEC 2048 +#define NFSD_MAY_OWNER_OVERRIDE 0x040 +#define NFSD_MAY_LOCAL_ACCESS 0x080 /* for device special files */ +#define NFSD_MAY_BYPASS_GSS_ON_ROOT 0x100 +#define NFSD_MAY_NOT_BREAK_LEASE 0x200 +#define NFSD_MAY_BYPASS_GSS 0x400 +#define NFSD_MAY_READ_IF_EXEC 0x800 #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) From 48483bf23a568f3ef4cc7ad2c8f1a082f10ad0e7 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 26 Aug 2011 20:40:28 -0400 Subject: [PATCH 018/103] nfsd4: simplify recovery dir setting Move around some of this code, simplify a bit. Reviewed-by: Boaz Harrosh Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4recover.c | 36 ++++++++++++++++++++++++++++++++---- fs/nfsd/nfs4state.c | 41 +---------------------------------------- fs/nfsd/state.h | 2 +- 3 files changed, 34 insertions(+), 45 deletions(-) diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 29d77f60585b..c3466610e6cd 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -45,6 +45,7 @@ /* Globals */ static struct file *rec_file; +static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; static int nfs4_save_creds(const struct cred **original_creds) @@ -354,13 +355,13 @@ nfsd4_recdir_load(void) { */ void -nfsd4_init_recdir(char *rec_dirname) +nfsd4_init_recdir() { const struct cred *original_cred; int status; printk("NFSD: Using %s as the NFSv4 state recovery directory\n", - rec_dirname); + user_recovery_dirname); BUG_ON(rec_file); @@ -372,10 +373,10 @@ nfsd4_init_recdir(char *rec_dirname) return; } - rec_file = filp_open(rec_dirname, O_RDONLY | O_DIRECTORY, 0); + rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); if (IS_ERR(rec_file)) { printk("NFSD: unable to find recovery directory %s\n", - rec_dirname); + user_recovery_dirname); rec_file = NULL; } @@ -390,3 +391,30 @@ nfsd4_shutdown_recdir(void) fput(rec_file); rec_file = NULL; } + +/* + * Change the NFSv4 recovery directory to recdir. + */ +int +nfs4_reset_recoverydir(char *recdir) +{ + int status; + struct path path; + + status = kern_path(recdir, LOOKUP_FOLLOW, &path); + if (status) + return status; + status = -ENOTDIR; + if (S_ISDIR(path.dentry->d_inode->i_mode)) { + strcpy(user_recovery_dirname, recdir); + status = 0; + } + path_put(&path); + return status; +} + +char * +nfs4_recoverydir(void) +{ + return user_recovery_dirname; +} diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index aa0a36e3b09e..36d0beb76864 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -64,8 +64,6 @@ static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); static struct nfs4_stateid * search_for_stateid(stateid_t *stid); static struct nfs4_delegation * search_for_delegation(stateid_t *stid); static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); -static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; -static void nfs4_set_recdir(char *recdir); static int check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner); /* Locking: */ @@ -4523,7 +4521,7 @@ nfsd4_load_reboot_recovery_data(void) int status; nfs4_lock_state(); - nfsd4_init_recdir(user_recovery_dirname); + nfsd4_init_recdir(); status = nfsd4_recdir_load(); nfs4_unlock_state(); if (status) @@ -4632,40 +4630,3 @@ nfs4_state_shutdown(void) nfs4_unlock_state(); nfsd4_destroy_callback_queue(); } - -/* - * user_recovery_dirname is protected by the nfsd_mutex since it's only - * accessed when nfsd is starting. - */ -static void -nfs4_set_recdir(char *recdir) -{ - strcpy(user_recovery_dirname, recdir); -} - -/* - * Change the NFSv4 recovery directory to recdir. - */ -int -nfs4_reset_recoverydir(char *recdir) -{ - int status; - struct path path; - - status = kern_path(recdir, LOOKUP_FOLLOW, &path); - if (status) - return status; - status = -ENOTDIR; - if (S_ISDIR(path.dentry->d_inode->i_mode)) { - nfs4_set_recdir(recdir); - status = 0; - } - path_put(&path); - return status; -} - -char * -nfs4_recoverydir(void) -{ - return user_recovery_dirname; -} diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 5cfebe504056..12c1b1ef52ec 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -467,7 +467,7 @@ extern void nfsd4_destroy_callback_queue(void); extern void nfsd4_shutdown_callback(struct nfs4_client *); extern void nfs4_put_delegation(struct nfs4_delegation *dp); extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); -extern void nfsd4_init_recdir(char *recdir_name); +extern void nfsd4_init_recdir(void); extern int nfsd4_recdir_load(void); extern void nfsd4_shutdown_recdir(void); extern int nfs4_client_to_reclaim(const char *name); From 6577aac01f00636c16cd583c30bd4dedf18475d5 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Fri, 12 Aug 2011 17:30:12 -0700 Subject: [PATCH 019/103] nfsd4: fix failure to end nfsd4 grace period Even if we fail to write a recovery record, we should still mark the client as having acquired its first state. Otherwise we leave 4.1 clients with indefinite ERR_GRACE returns. However, an inability to write stable storage records may cause failures of reboot recovery, and the problem should still be brought to the server administrator's attention. So, make sure the error is logged. These errors shouldn't normally be triggered on a corectly functioning server--this isn't a case where a misconfigured client could spam the logs. Signed-off-by: Boaz Harrosh Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4recover.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index c3466610e6cd..493851b844fe 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -130,6 +130,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) if (!rec_file || clp->cl_firststate) return 0; + clp->cl_firststate = 1; status = nfs4_save_creds(&original_cred); if (status < 0) return status; @@ -144,10 +145,8 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) goto out_unlock; } status = -EEXIST; - if (dentry->d_inode) { - dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n"); + if (dentry->d_inode) goto out_put; - } status = mnt_want_write(rec_file->f_path.mnt); if (status) goto out_put; @@ -157,12 +156,14 @@ out_put: dput(dentry); out_unlock: mutex_unlock(&dir->d_inode->i_mutex); - if (status == 0) { - clp->cl_firststate = 1; + if (status == 0) vfs_fsync(rec_file, 0); - } + else + printk(KERN_ERR "NFSD: failed to write recovery record" + " (err %d); please check that %s exists" + " and is writeable", status, + user_recovery_dirname); nfs4_reset_creds(original_cred); - dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status); return status; } From 3e77246393c0a433247631a1f0e9ec98d3d78a1c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 10 Aug 2011 19:07:33 -0400 Subject: [PATCH 020/103] nfsd4: stop using nfserr_resource for transitory errors The server is returning nfserr_resource for both permanent errors and for errors (like allocation failures) that might be resolved by retrying later. Save nfserr_resource for the former and use delay/jukebox for the latter. Cc: stable@kernel.org Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 2 +- fs/nfsd/nfs4recover.c | 2 +- fs/nfsd/nfs4state.c | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index b5530984db91..50bae7471147 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -940,7 +940,7 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, count = 4 + (verify->ve_attrlen >> 2); buf = kmalloc(count << 2, GFP_KERNEL); if (!buf) - return nfserr_resource; + return nfserr_jukebox; status = nfsd4_encode_fattr(&cstate->current_fh, cstate->current_fh.fh_export, diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 493851b844fe..ed083b9a731b 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -89,7 +89,7 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) struct xdr_netobj cksum; struct hash_desc desc; struct scatterlist sg; - __be32 status = nfserr_resource; + __be32 status = nfserr_jukebox; dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", clname->len, clname->data); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 36d0beb76864..c7d54f6a19c9 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1944,7 +1944,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * of 5 bullet points, labeled as CASE0 - CASE4 below. */ unconf = find_unconfirmed_client_by_str(dname, strhashval); - status = nfserr_resource; + status = nfserr_jukebox; if (!conf) { /* * RFC 3530 14.2.33 CASE 4: @@ -2481,7 +2481,7 @@ renew: if (open->op_stateowner == NULL) { sop = alloc_init_open_stateowner(strhashval, clp, open); if (sop == NULL) - return nfserr_resource; + return nfserr_jukebox; open->op_stateowner = sop; } list_del_init(&sop->so_close_lru); @@ -2617,7 +2617,7 @@ nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, stp = nfs4_alloc_stateid(); if (stp == NULL) - return nfserr_resource; + return nfserr_jukebox; status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open); if (status) { @@ -2848,7 +2848,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status = nfserr_bad_stateid; if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) goto out; - status = nfserr_resource; + status = nfserr_jukebox; fp = alloc_init_file(ino); if (fp == NULL) goto out; @@ -4033,7 +4033,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* XXX: Do we need to check for duplicate stateowners on * the same file, or should they just be allowed (and * create new stateids)? */ - status = nfserr_resource; + status = nfserr_jukebox; lock_sop = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock); if (lock_sop == NULL) @@ -4117,9 +4117,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, case (EDEADLK): status = nfserr_deadlock; break; - default: + default: dprintk("NFSD: nfsd4_lock: vfs_lock_file() failed! status %d\n",err); - status = nfserr_resource; + status = nfserrno(err); break; } out: From ddc04c41636f8cd374d72cdd3a0fcaa916fbc5d0 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 30 Jul 2011 23:46:29 -0400 Subject: [PATCH 021/103] nfsd4: replace some macros by functions For all the usual reasons. (Type safety, readability.) Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 53 ++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c7d54f6a19c9..ce8e70cd04c5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -139,10 +139,19 @@ unsigned int max_delegations; #define OWNER_HASH_SIZE (1 << OWNER_HASH_BITS) #define OWNER_HASH_MASK (OWNER_HASH_SIZE - 1) -#define ownerid_hashval(id) \ - ((id) & OWNER_HASH_MASK) -#define ownerstr_hashval(clientid, ownername) \ - (((clientid) + opaque_hashval((ownername.data), (ownername.len))) & OWNER_HASH_MASK) +static unsigned int ownerid_hashval(const u32 id) +{ + return id & OWNER_HASH_MASK; +} + +static unsigned int ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) +{ + unsigned int ret; + + ret = opaque_hashval(ownername->data, ownername->len); + ret += clientid; + return ret & OWNER_HASH_MASK; +} static struct list_head ownerid_hashtbl[OWNER_HASH_SIZE]; static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; @@ -156,10 +165,16 @@ static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; #define STATEID_HASH_SIZE (1 << STATEID_HASH_BITS) #define STATEID_HASH_MASK (STATEID_HASH_SIZE - 1) -#define file_hashval(x) \ - hash_ptr(x, FILE_HASH_BITS) -#define stateid_hashval(owner_id, file_id) \ - (((owner_id) + (file_id)) & STATEID_HASH_MASK) +static unsigned int file_hashval(struct inode *ino) +{ + /* XXX: why are we hashing on inode pointer, anyway? */ + return hash_ptr(ino, FILE_HASH_BITS); +} + +static unsigned int stateid_hashval(u32 owner_id, u32 file_id) +{ + return (owner_id + file_id) & STATEID_HASH_MASK; +} static struct list_head file_hashtbl[FILE_HASH_SIZE]; static struct list_head stateid_hashtbl[STATEID_HASH_SIZE]; @@ -290,10 +305,16 @@ static DEFINE_SPINLOCK(client_lock); #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) #define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1) -#define clientid_hashval(id) \ - ((id) & CLIENT_HASH_MASK) -#define clientstr_hashval(name) \ - (opaque_hashval((name), 8) & CLIENT_HASH_MASK) +static unsigned int clientid_hashval(u32 id) +{ + return id & CLIENT_HASH_MASK; +} + +static unsigned int clientstr_hashval(const char *name) +{ + return opaque_hashval(name, 8) & CLIENT_HASH_MASK; +} + /* * reclaim_str_hashtbl[] holds known client info from previous reset/reboot * used in reboot/reset lease grace period processing @@ -2443,7 +2464,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, if (STALE_CLIENTID(&open->op_clientid)) return nfserr_stale_clientid; - strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner); + strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); sop = find_openstateowner_str(strhashval, open); open->op_stateowner = sop; if (!sop) { @@ -3711,8 +3732,10 @@ last_byte_offset(u64 start, u64 len) return end > start ? end - 1: NFS4_MAX_UINT64; } -#define lockownerid_hashval(id) \ - ((id) & LOCK_HASH_MASK) +static unsigned int lockownerid_hashval(u32 id) +{ + return id & LOCK_HASH_MASK; +} static inline unsigned int lock_ownerstr_hashval(struct inode *inode, u32 cl_id, From 506f275fffcaceb8abe3db69e581d86c7da48a82 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 11 Aug 2011 18:50:18 -0400 Subject: [PATCH 022/103] nfsd4: name openowner data structures more clearly These appear to be generic (for both open and lock owners), but they're actually just for open owners. This has confused me more than once. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ce8e70cd04c5..dd6424face32 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -134,27 +134,27 @@ unsigned int max_delegations; * Open owner state (share locks) */ -/* hash tables for nfs4_stateowner */ -#define OWNER_HASH_BITS 8 -#define OWNER_HASH_SIZE (1 << OWNER_HASH_BITS) -#define OWNER_HASH_MASK (OWNER_HASH_SIZE - 1) +/* hash tables for open owners */ +#define OPEN_OWNER_HASH_BITS 8 +#define OPEN_OWNER_HASH_SIZE (1 << OPEN_OWNER_HASH_BITS) +#define OPEN_OWNER_HASH_MASK (OPEN_OWNER_HASH_SIZE - 1) -static unsigned int ownerid_hashval(const u32 id) +static unsigned int open_ownerid_hashval(const u32 id) { - return id & OWNER_HASH_MASK; + return id & OPEN_OWNER_HASH_MASK; } -static unsigned int ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) +static unsigned int open_ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) { unsigned int ret; ret = opaque_hashval(ownername->data, ownername->len); ret += clientid; - return ret & OWNER_HASH_MASK; + return ret & OPEN_OWNER_HASH_MASK; } -static struct list_head ownerid_hashtbl[OWNER_HASH_SIZE]; -static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; +static struct list_head open_ownerid_hashtbl[OPEN_OWNER_HASH_SIZE]; +static struct list_head open_ownerstr_hashtbl[OPEN_OWNER_HASH_SIZE]; /* hash table for nfs4_file */ #define FILE_HASH_BITS 8 @@ -2240,7 +2240,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str if (!(sop = alloc_stateowner(&open->op_owner))) return NULL; - idhashval = ownerid_hashval(current_ownerid); + idhashval = open_ownerid_hashval(current_ownerid); INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); INIT_LIST_HEAD(&sop->so_perclient); @@ -2248,8 +2248,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str INIT_LIST_HEAD(&sop->so_perstateid); /* not used */ INIT_LIST_HEAD(&sop->so_close_lru); sop->so_time = 0; - list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); - list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); + list_add(&sop->so_idhash, &open_ownerid_hashtbl[idhashval]); + list_add(&sop->so_strhash, &open_ownerstr_hashtbl[strhashval]); list_add(&sop->so_perclient, &clp->cl_openowners); sop->so_is_open_owner = 1; sop->so_id = current_ownerid++; @@ -2313,7 +2313,7 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open) { struct nfs4_stateowner *so = NULL; - list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { + list_for_each_entry(so, &open_ownerstr_hashtbl[hashval], so_strhash) { if (same_owner_str(so, &open->op_owner, &open->op_clientid)) return so; } @@ -2464,7 +2464,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, if (STALE_CLIENTID(&open->op_clientid)) return nfserr_stale_clientid; - strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); + strhashval = open_ownerstr_hashval(clientid->cl_id, &open->op_owner); sop = find_openstateowner_str(strhashval, open); open->op_stateowner = sop; if (!sop) { @@ -4518,9 +4518,9 @@ nfs4_state_init(void) for (i = 0; i < FILE_HASH_SIZE; i++) { INIT_LIST_HEAD(&file_hashtbl[i]); } - for (i = 0; i < OWNER_HASH_SIZE; i++) { - INIT_LIST_HEAD(&ownerstr_hashtbl[i]); - INIT_LIST_HEAD(&ownerid_hashtbl[i]); + for (i = 0; i < OPEN_OWNER_HASH_SIZE; i++) { + INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]); + INIT_LIST_HEAD(&open_ownerid_hashtbl[i]); } for (i = 0; i < STATEID_HASH_SIZE; i++) { INIT_LIST_HEAD(&stateid_hashtbl[i]); From ff194bd95959ea9047d536b4f4ad6a992754e48d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 12 Aug 2011 09:42:57 -0400 Subject: [PATCH 023/103] nfsd4: cleanup lock/stateowner initialization Share some common code, stop doing silly things like initializing a list head immediately before adding it to a list, etc. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 102 +++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index dd6424face32..3e64288399f7 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2215,51 +2215,61 @@ nfs4_free_stateowner(struct kref *kref) kmem_cache_free(stateowner_slab, sop); } -static inline struct nfs4_stateowner * -alloc_stateowner(struct xdr_netobj *owner) +static void init_nfs4_replay(struct nfs4_replay *rp) +{ + rp->rp_status = nfserr_serverfault; + rp->rp_buflen = 0; + rp->rp_buf = rp->rp_ibuf; +} + +static inline struct nfs4_stateowner *alloc_stateowner(struct xdr_netobj *owner, struct nfs4_client *clp) { struct nfs4_stateowner *sop; - if ((sop = kmem_cache_alloc(stateowner_slab, GFP_KERNEL))) { - if ((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) { - memcpy(sop->so_owner.data, owner->data, owner->len); - sop->so_owner.len = owner->len; - kref_init(&sop->so_ref); - return sop; - } + sop = kmem_cache_alloc(stateowner_slab, GFP_KERNEL); + if (!sop) + return NULL; + + sop->so_owner.data = kmemdup(owner->data, owner->len, GFP_KERNEL); + if (!sop->so_owner.data) { kmem_cache_free(stateowner_slab, sop); + return NULL; } - return NULL; + sop->so_owner.len = owner->len; + + kref_init(&sop->so_ref); + INIT_LIST_HEAD(&sop->so_perclient); + INIT_LIST_HEAD(&sop->so_stateids); + INIT_LIST_HEAD(&sop->so_perstateid); + INIT_LIST_HEAD(&sop->so_close_lru); + sop->so_id = current_ownerid++; + sop->so_time = 0; + sop->so_client = clp; + init_nfs4_replay(&sop->so_replay); + return sop; +} + +static void hash_openowner(struct nfs4_stateowner *sop, struct nfs4_client *clp, unsigned int strhashval) +{ + unsigned int idhashval; + + idhashval = open_ownerid_hashval(sop->so_id); + list_add(&sop->so_idhash, &open_ownerid_hashtbl[idhashval]); + list_add(&sop->so_strhash, &open_ownerstr_hashtbl[strhashval]); + list_add(&sop->so_perclient, &clp->cl_openowners); } static struct nfs4_stateowner * alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) { struct nfs4_stateowner *sop; - struct nfs4_replay *rp; - unsigned int idhashval; - if (!(sop = alloc_stateowner(&open->op_owner))) + sop = alloc_stateowner(&open->op_owner, clp); + if (!sop) return NULL; - idhashval = open_ownerid_hashval(current_ownerid); - INIT_LIST_HEAD(&sop->so_idhash); - INIT_LIST_HEAD(&sop->so_strhash); - INIT_LIST_HEAD(&sop->so_perclient); - INIT_LIST_HEAD(&sop->so_stateids); - INIT_LIST_HEAD(&sop->so_perstateid); /* not used */ - INIT_LIST_HEAD(&sop->so_close_lru); - sop->so_time = 0; - list_add(&sop->so_idhash, &open_ownerid_hashtbl[idhashval]); - list_add(&sop->so_strhash, &open_ownerstr_hashtbl[strhashval]); - list_add(&sop->so_perclient, &clp->cl_openowners); sop->so_is_open_owner = 1; - sop->so_id = current_ownerid++; - sop->so_client = clp; sop->so_seqid = open->op_seqid; sop->so_confirmed = 0; - rp = &sop->so_replay; - rp->rp_status = nfserr_serverfault; - rp->rp_buflen = 0; - rp->rp_buf = rp->rp_ibuf; + hash_openowner(sop, clp, strhashval); return sop; } @@ -3902,6 +3912,16 @@ find_lockstateowner_str(struct inode *inode, clientid_t *clid, return NULL; } +static void hash_lockowner(struct nfs4_stateowner *sop, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp) +{ + unsigned int idhashval; + + idhashval = lockownerid_hashval(sop->so_id); + list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); + list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); + list_add(&sop->so_perstateid, &open_stp->st_lockowners); +} + /* * Alloc a lock owner structure. * Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has @@ -3913,33 +3933,17 @@ find_lockstateowner_str(struct inode *inode, clientid_t *clid, static struct nfs4_stateowner * alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp, struct nfsd4_lock *lock) { struct nfs4_stateowner *sop; - struct nfs4_replay *rp; - unsigned int idhashval; - if (!(sop = alloc_stateowner(&lock->lk_new_owner))) + sop = alloc_stateowner(&lock->lk_new_owner, clp); + if (!sop) return NULL; - idhashval = lockownerid_hashval(current_ownerid); - INIT_LIST_HEAD(&sop->so_idhash); - INIT_LIST_HEAD(&sop->so_strhash); - INIT_LIST_HEAD(&sop->so_perclient); INIT_LIST_HEAD(&sop->so_stateids); - INIT_LIST_HEAD(&sop->so_perstateid); - INIT_LIST_HEAD(&sop->so_close_lru); /* not used */ - sop->so_time = 0; - list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); - list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); - list_add(&sop->so_perstateid, &open_stp->st_lockowners); sop->so_is_open_owner = 0; - sop->so_id = current_ownerid++; - sop->so_client = clp; /* It is the openowner seqid that will be incremented in encode in the * case of new lockowners; so increment the lock seqid manually: */ sop->so_seqid = lock->lk_new_lock_seqid + 1; sop->so_confirmed = 1; - rp = &sop->so_replay; - rp->rp_status = nfserr_serverfault; - rp->rp_buflen = 0; - rp->rp_buf = rp->rp_ibuf; + hash_lockowner(sop, strhashval, clp, open_stp); return sop; } From 28dde241cc65c9464b7627d9a9ed3a66e4df2586 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 22 Aug 2011 10:07:12 -0400 Subject: [PATCH 024/103] nfsd4: remove HAS_SESSION This flag doesn't really buy us anything. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 30 ++++++++++-------------------- fs/nfsd/state.h | 3 +-- fs/nfsd/xdr4.h | 2 +- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3e64288399f7..14c8dd64e136 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3168,13 +3168,13 @@ grace_disallows_io(struct inode *inode) return locks_in_grace() && mandatory_lock(inode); } -static int check_stateid_generation(stateid_t *in, stateid_t *ref, int flags) +static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session) { /* * When sessions are used the stateid generation number is ignored * when it is zero. */ - if ((flags & HAS_SESSION) && in->si_generation == 0) + if (has_session && in->si_generation == 0) goto out; /* If the client sends us a stateid from the future, it's buggy: */ @@ -3206,7 +3206,7 @@ static int is_open_stateid(struct nfs4_stateid *stateid) return stateid->st_openstp == NULL; } -__be32 nfs4_validate_stateid(stateid_t *stateid, int flags) +__be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) { struct nfs4_stateid *stp = NULL; __be32 status = nfserr_stale_stateid; @@ -3223,7 +3223,7 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, int flags) if (!stp->st_stateowner->so_confirmed) goto out; - status = check_stateid_generation(stateid, &stp->st_stateid, flags); + status = check_stateid_generation(stateid, &stp->st_stateid, has_session); if (status) goto out; @@ -3251,9 +3251,6 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, if (grace_disallows_io(ino)) return nfserr_grace; - if (nfsd4_has_session(cstate)) - flags |= HAS_SESSION; - if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) return check_special_stateids(current_fh, stateid, flags); @@ -3270,8 +3267,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, dp = find_delegation_stateid(ino, stateid); if (!dp) goto out; - status = check_stateid_generation(stateid, &dp->dl_stateid, - flags); + status = check_stateid_generation(stateid, &dp->dl_stateid, nfsd4_has_session(cstate)); if (status) goto out; status = nfs4_check_delegmode(dp, flags); @@ -3292,7 +3288,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, if (!stp->st_stateowner->so_confirmed) goto out; status = check_stateid_generation(stateid, &stp->st_stateid, - flags); + nfsd4_has_session(cstate)); if (status) goto out; status = nfs4_check_openmode(stp, flags); @@ -3421,9 +3417,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, if (STALE_STATEID(stateid)) return nfserr_stale_stateid; - if (nfsd4_has_session(cstate)) - flags |= HAS_SESSION; - /* * We return BAD_STATEID if filehandle doesn't match stateid, * the confirmed flag is incorrecly set, or the generation @@ -3457,7 +3450,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, if (lock->lk_is_new) { if (!sop->so_is_open_owner) return nfserr_bad_stateid; - if (!(flags & HAS_SESSION) && + if (!nfsd4_has_session(cstate) && !same_clid(&clp->cl_clientid, lockclid)) return nfserr_bad_stateid; /* stp is the open stateid */ @@ -3482,7 +3475,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, * For the moment, we ignore the possibility of * generation number wraparound. */ - if (!(flags & HAS_SESSION) && seqid != sop->so_seqid) + if (!nfsd4_has_session(cstate) && seqid != sop->so_seqid) goto check_replay; if (sop->so_confirmed && flags & CONFIRM) { @@ -3495,7 +3488,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, " confirmed yet!\n"); return nfserr_bad_stateid; } - status = check_stateid_generation(stateid, &stp->st_stateid, flags); + status = check_stateid_generation(stateid, &stp->st_stateid, nfsd4_has_session(cstate)); if (status) return status; renew_client(sop->so_client); @@ -3679,14 +3672,11 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stateid_t *stateid = &dr->dr_stateid; struct inode *inode; __be32 status; - int flags = 0; if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) return status; inode = cstate->current_fh.fh_dentry->d_inode; - if (nfsd4_has_session(cstate)) - flags |= HAS_SESSION; nfs4_lock_state(); status = nfserr_bad_stateid; if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) @@ -3701,7 +3691,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, dp = find_delegation_stateid(inode, stateid); if (!dp) goto out; - status = check_stateid_generation(stateid, &dp->dl_stateid, flags); + status = check_stateid_generation(stateid, &dp->dl_stateid, nfsd4_has_session(cstate)); if (status) goto out; renew_client(dp->dl_client); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 12c1b1ef52ec..f02badd70cf2 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -439,7 +439,6 @@ struct nfs4_stateid { }; /* flags for preprocess_seqid_op() */ -#define HAS_SESSION 0x00000001 #define CONFIRM 0x00000002 #define OPEN_STATE 0x00000004 #define LOCK_STATE 0x00000008 @@ -476,7 +475,7 @@ extern void nfsd4_recdir_purge_old(void); extern int nfsd4_create_clid_dir(struct nfs4_client *clp); extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); extern void release_session_client(struct nfsd4_session *); -extern __be32 nfs4_validate_stateid(stateid_t *, int); +extern __be32 nfs4_validate_stateid(stateid_t *, bool); static inline void nfs4_put_stateowner(struct nfs4_stateowner *so) diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index d2a8d04428c7..663193b21a24 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -351,7 +351,7 @@ struct nfsd4_saved_compoundargs { struct nfsd4_test_stateid { __be32 ts_num_ids; - __be32 ts_has_session; + bool ts_has_session; struct nfsd4_compoundargs *ts_saved_args; struct nfsd4_saved_compoundargs ts_savedp; }; From a9004abc34239705840eaf6fe3d6cc9cb7656cba Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 23 Aug 2011 15:43:04 -0400 Subject: [PATCH 025/103] nfsd4: cleanup and consolidate seqid_mutating_err Signed-off-by: J. Bruce Fields --- fs/nfs/nfs4_fs.h | 24 ------------------------ fs/nfsd/nfs4xdr.c | 14 +------------- include/linux/nfs4.h | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 37 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 1ec1a85fa71c..1a652a0bd7db 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -13,30 +13,6 @@ struct idmap; -/* - * In a seqid-mutating op, this macro controls which error return - * values trigger incrementation of the seqid. - * - * from rfc 3010: - * The client MUST monotonically increment the sequence number for the - * CLOSE, LOCK, LOCKU, OPEN, OPEN_CONFIRM, and OPEN_DOWNGRADE - * operations. This is true even in the event that the previous - * operation that used the sequence number received an error. The only - * exception to this rule is if the previous operation received one of - * the following errors: NFSERR_STALE_CLIENTID, NFSERR_STALE_STATEID, - * NFSERR_BAD_STATEID, NFSERR_BAD_SEQID, NFSERR_BADXDR, - * NFSERR_RESOURCE, NFSERR_NOFILEHANDLE. - * - */ -#define seqid_mutating_err(err) \ -(((err) != NFSERR_STALE_CLIENTID) && \ - ((err) != NFSERR_STALE_STATEID) && \ - ((err) != NFSERR_BAD_STATEID) && \ - ((err) != NFSERR_BAD_SEQID) && \ - ((err) != NFSERR_BAD_XDR) && \ - ((err) != NFSERR_RESOURCE) && \ - ((err) != NFSERR_NOFILEHANDLE)) - enum nfs4_client_state { NFS4CLNT_MANAGER_RUNNING = 0, NFS4CLNT_CHECK_LEASE, diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 78c792fb59a8..04ad9a2ca3d0 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1623,18 +1623,6 @@ static void write_cinfo(__be32 **p, struct nfsd4_change_info *c) \ save = resp->p; -static bool seqid_mutating_err(__be32 err) -{ - /* rfc 3530 section 8.1.5: */ - return err != nfserr_stale_clientid && - err != nfserr_stale_stateid && - err != nfserr_bad_stateid && - err != nfserr_bad_seqid && - err != nfserr_bad_xdr && - err != nfserr_resource && - err != nfserr_nofilehandle; -} - /* * Routine for encoding the result of a "seqid-mutating" NFSv4 operation. This * is where sequence id's are incremented, and the replay cache is filled. @@ -1643,7 +1631,7 @@ static bool seqid_mutating_err(__be32 err) */ #define ENCODE_SEQID_OP_TAIL(stateowner) do { \ - if (seqid_mutating_err(nfserr) && stateowner) { \ + if (seqid_mutating_err(ntohl(nfserr)) && stateowner) { \ stateowner->so_seqid++; \ stateowner->so_replay.rp_status = nfserr; \ stateowner->so_replay.rp_buflen = \ diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 76f99e8714f3..b875b0324fc0 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -373,6 +373,22 @@ enum nfsstat4 { NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */ }; +static inline bool seqid_mutating_err(u32 err) +{ + /* rfc 3530 section 8.1.5: */ + switch (err) { + case NFS4ERR_STALE_CLIENTID: + case NFS4ERR_STALE_STATEID: + case NFS4ERR_BAD_STATEID: + case NFS4ERR_BAD_SEQID: + case NFS4ERR_BADXDR: + case NFS4ERR_RESOURCE: + case NFS4ERR_NOFILEHANDLE: + return false; + }; + return true; +} + /* * Note: NF4BAD is not actually part of the protocol; it is just used * internally by nfsd. From 9afb978400e65ea6a3200b3e163606ce86e13c25 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 22 Aug 2011 11:39:07 -0400 Subject: [PATCH 026/103] nfsd4: simplify lock openmode check Note that the special handling for the lock stateid case is already done by nfs4_check_openmode() (as of 02921914170e3b7fea1cd82dac9713685d2de5e2 "nfsd4: fix openmode checking on IO using lock stateid") so we no longer need these two cases in the caller. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 14c8dd64e136..aafc41a0a8d0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3453,16 +3453,11 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, if (!nfsd4_has_session(cstate) && !same_clid(&clp->cl_clientid, lockclid)) return nfserr_bad_stateid; - /* stp is the open stateid */ - status = nfs4_check_openmode(stp, lkflg); - if (status) - return status; - } else { - /* stp is the lock stateid */ - status = nfs4_check_openmode(stp->st_openstp, lkflg); - if (status) - return status; - } + } + /* stp is the open stateid */ + status = nfs4_check_openmode(stp, lkflg); + if (status) + return status; } if (nfs4_check_fh(current_fh, stp)) { From b34f27aa5da75b0b6c054e76bb4b92aea7aac04b Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 22 Aug 2011 13:13:31 -0400 Subject: [PATCH 027/103] nfsd4: get lock checks out of preprocess_seqid_op We've got some lock-specific code here in nfs4_preprocess_seqid_op which is only used by nfsd4_lock(). Move it to the caller. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 49 ++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index aafc41a0a8d0..d2bf80d8d85b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3396,7 +3396,7 @@ static __be32 nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, - struct nfs4_stateid **stpp, struct nfsd4_lock *lock) + struct nfs4_stateid **stpp) { struct nfs4_stateid *stp; struct nfs4_stateowner *sop; @@ -3439,27 +3439,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, *stpp = stp; *sopp = sop = stp->st_stateowner; - if (lock) { - clientid_t *lockclid = &lock->v.new.clientid; - struct nfs4_client *clp = sop->so_client; - int lkflg = 0; - __be32 status; - - lkflg = setlkflg(lock->lk_type); - - if (lock->lk_is_new) { - if (!sop->so_is_open_owner) - return nfserr_bad_stateid; - if (!nfsd4_has_session(cstate) && - !same_clid(&clp->cl_clientid, lockclid)) - return nfserr_bad_stateid; - } - /* stp is the open stateid */ - status = nfs4_check_openmode(stp, lkflg); - if (status) - return status; - } - if (nfs4_check_fh(current_fh, stp)) { dprintk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n"); return nfserr_bad_stateid; @@ -3522,7 +3501,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if ((status = nfs4_preprocess_seqid_op(cstate, oc->oc_seqid, &oc->oc_req_stateid, CONFIRM | OPEN_STATE, - &oc->oc_stateowner, &stp, NULL))) + &oc->oc_stateowner, &stp))) goto out; sop = oc->oc_stateowner; @@ -3585,7 +3564,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, od->od_seqid, &od->od_stateid, OPEN_STATE, - &od->od_stateowner, &stp, NULL))) + &od->od_stateowner, &stp))) goto out; status = nfserr_inval; @@ -3635,7 +3614,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, close->cl_seqid, &close->cl_stateid, OPEN_STATE | CLOSE_STATE, - &close->cl_stateowner, &stp, NULL))) + &close->cl_stateowner, &stp))) goto out; status = nfs_ok; update_stateid(&stp->st_stateid); @@ -3997,6 +3976,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock conflock; __be32 status = 0; unsigned int strhashval; + int lkflg; int err; dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", @@ -4032,11 +4012,17 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, lock->lk_new_open_seqid, &lock->lk_new_open_stateid, OPEN_STATE, - &lock->lk_replay_owner, &open_stp, - lock); + &lock->lk_replay_owner, &open_stp); if (status) goto out; + status = nfserr_bad_stateid; open_sop = lock->lk_replay_owner; + if (!open_sop->so_is_open_owner) + goto out; + if (!nfsd4_has_session(cstate) && + !same_clid(&open_sop->so_client->cl_clientid, + &lock->v.new.clientid)) + goto out; /* create lockowner and lock stateid */ fp = open_stp->st_file; strhashval = lock_ownerstr_hashval(fp->fi_inode, @@ -4059,7 +4045,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, lock->lk_old_lock_seqid, &lock->lk_old_lock_stateid, LOCK_STATE, - &lock->lk_replay_owner, &lock_stp, lock); + &lock->lk_replay_owner, &lock_stp); if (status) goto out; lock_sop = lock->lk_replay_owner; @@ -4067,6 +4053,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } /* lock->lk_replay_owner and lock_stp have been created or found */ + lkflg = setlkflg(lock->lk_type); + status = nfs4_check_openmode(lock_stp, lkflg); + if (status) + goto out; + status = nfserr_grace; if (locks_in_grace() && !lock->lk_reclaim) goto out; @@ -4259,7 +4250,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, locku->lu_seqid, &locku->lu_stateid, LOCK_STATE, - &locku->lu_stateowner, &stp, NULL))) + &locku->lu_stateowner, &stp))) goto out; filp = find_any_file(stp->st_file); From 3cc9fda40a427aeb176bab898edca4e9a3ada524 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 23 Aug 2011 15:17:50 -0400 Subject: [PATCH 028/103] nfsd4: remove redundant is_open_owner check When called with OPEN_STATE, preprocess_seqid_op only returns an open stateid, hence only an open owner. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d2bf80d8d85b..d2b637b717c3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4017,8 +4017,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; status = nfserr_bad_stateid; open_sop = lock->lk_replay_owner; - if (!open_sop->so_is_open_owner) - goto out; if (!nfsd4_has_session(cstate) && !same_clid(&open_sop->so_client->cl_clientid, &lock->v.new.clientid)) From c152292f9ee7eb4ed30edc0bd5027a5beef5f5e8 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 26 Aug 2011 17:22:06 -0400 Subject: [PATCH 029/103] nfsd: remove include/linux/nfsd/syscall.h We don't need this any more. Signed-off-by: J. Bruce Fields --- fs/compat.c | 1 - fs/nfsd/export.c | 1 - fs/nfsd/nfsctl.c | 1 - include/linux/nfsd/Kbuild | 1 - include/linux/nfsd/syscall.h | 116 ----------------------------------- 5 files changed, 120 deletions(-) delete mode 100644 include/linux/nfsd/syscall.h diff --git a/fs/compat.c b/fs/compat.c index 0b48d018e38a..f2b36d472c73 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index f4cc1e2bfc54..d491421cd708 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -16,7 +16,6 @@ #include #include -#include #include #include "nfsd.h" diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index c7716143cbd1..db34a585e112 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include diff --git a/include/linux/nfsd/Kbuild b/include/linux/nfsd/Kbuild index 0e528606d46f..b8d4001212b3 100644 --- a/include/linux/nfsd/Kbuild +++ b/include/linux/nfsd/Kbuild @@ -2,4 +2,3 @@ header-y += debug.h header-y += export.h header-y += nfsfh.h header-y += stats.h -header-y += syscall.h diff --git a/include/linux/nfsd/syscall.h b/include/linux/nfsd/syscall.h deleted file mode 100644 index 812bc1e160dc..000000000000 --- a/include/linux/nfsd/syscall.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * include/linux/nfsd/syscall.h - * - * This file holds all declarations for the knfsd syscall interface. - * - * Copyright (C) 1995-1997 Olaf Kirch - */ - -#ifndef NFSD_SYSCALL_H -#define NFSD_SYSCALL_H - -#include -#include - -/* - * Version of the syscall interface - */ -#define NFSCTL_VERSION 0x0201 - -/* - * These are the commands understood by nfsctl(). - */ -#define NFSCTL_SVC 0 /* This is a server process. */ -#define NFSCTL_ADDCLIENT 1 /* Add an NFS client. */ -#define NFSCTL_DELCLIENT 2 /* Remove an NFS client. */ -#define NFSCTL_EXPORT 3 /* export a file system. */ -#define NFSCTL_UNEXPORT 4 /* unexport a file system. */ -/*#define NFSCTL_UGIDUPDATE 5 / * update a client's uid/gid map. DISCARDED */ -/*#define NFSCTL_GETFH 6 / * get an fh by ino DISCARDED */ -#define NFSCTL_GETFD 7 /* get an fh by path (used by mountd) */ -#define NFSCTL_GETFS 8 /* get an fh by path with max FH len */ - -/* SVC */ -struct nfsctl_svc { - unsigned short svc_port; - int svc_nthreads; -}; - -/* ADDCLIENT/DELCLIENT */ -struct nfsctl_client { - char cl_ident[NFSCLNT_IDMAX+1]; - int cl_naddr; - struct in_addr cl_addrlist[NFSCLNT_ADDRMAX]; - int cl_fhkeytype; - int cl_fhkeylen; - unsigned char cl_fhkey[NFSCLNT_KEYMAX]; -}; - -/* EXPORT/UNEXPORT */ -struct nfsctl_export { - char ex_client[NFSCLNT_IDMAX+1]; - char ex_path[NFS_MAXPATHLEN+1]; - __kernel_old_dev_t ex_dev; - __kernel_ino_t ex_ino; - int ex_flags; - __kernel_uid_t ex_anon_uid; - __kernel_gid_t ex_anon_gid; -}; - -/* GETFD */ -struct nfsctl_fdparm { - struct sockaddr gd_addr; - char gd_path[NFS_MAXPATHLEN+1]; - int gd_version; -}; - -/* GETFS - GET Filehandle with Size */ -struct nfsctl_fsparm { - struct sockaddr gd_addr; - char gd_path[NFS_MAXPATHLEN+1]; - int gd_maxlen; -}; - -/* - * This is the argument union. - */ -struct nfsctl_arg { - int ca_version; /* safeguard */ - union { - struct nfsctl_svc u_svc; - struct nfsctl_client u_client; - struct nfsctl_export u_export; - struct nfsctl_fdparm u_getfd; - struct nfsctl_fsparm u_getfs; - /* - * The following dummy member is needed to preserve binary compatibility - * on platforms where alignof(void*)>alignof(int). It's needed because - * this union used to contain a member (u_umap) which contained a - * pointer. - */ - void *u_ptr; - } u; -#define ca_svc u.u_svc -#define ca_client u.u_client -#define ca_export u.u_export -#define ca_getfd u.u_getfd -#define ca_getfs u.u_getfs -}; - -union nfsctl_res { - __u8 cr_getfh[NFS_FHSIZE]; - struct knfsd_fh cr_getfs; -}; - -#ifdef __KERNEL__ -/* - * Kernel syscall implementation. - */ -extern int exp_addclient(struct nfsctl_client *ncp); -extern int exp_delclient(struct nfsctl_client *ncp); -extern int exp_export(struct nfsctl_export *nxp); -extern int exp_unexport(struct nfsctl_export *nxp); - -#endif /* __KERNEL__ */ - -#endif /* NFSD_SYSCALL_H */ From b7d7ca35807b4c8ca3271885b47e67c843376f77 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 31 Aug 2011 15:39:30 -0400 Subject: [PATCH 030/103] nfsd4: fix off-by-one-error in SEQUENCE reply The values here represent highest slotid numbers. Since slotid's are numbered starting from zero, the highest should be one less than the number of slots. Reported-by: Rick Macklem Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 04ad9a2ca3d0..f982d8574d95 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3221,9 +3221,9 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr, WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN); WRITE32(seq->seqid); WRITE32(seq->slotid); - WRITE32(seq->maxslots); - /* For now: target_maxslots = maxslots */ - WRITE32(seq->maxslots); + /* Note slotid's are numbered from zero: */ + WRITE32(seq->maxslots - 1); /* sr_highest_slotid */ + WRITE32(seq->maxslots - 1); /* sr_target_highest_slotid */ WRITE32(seq->status_flags); ADJUST_ARGS(); From c2d8eb7ac645e1baba7205cb2631e2f21db3d6a9 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 29 Aug 2011 10:36:11 -0400 Subject: [PATCH 031/103] nfsd4: remove typoed replay field Wow, I wonder how long that typo's been there. Signed-off-by: J. Bruce Fields --- fs/nfsd/state.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index f02badd70cf2..6b706a60ce88 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -312,7 +312,6 @@ struct nfs4_replay { __be32 rp_status; unsigned int rp_buflen; char *rp_buf; - unsigned intrp_allocated; struct knfsd_fh rp_openfh; char rp_ibuf[NFSD4_REPLAY_ISIZE]; }; From 5fa0bbb4ee5481a6b3e83c4968142ca433d71914 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 31 Aug 2011 15:25:46 -0400 Subject: [PATCH 032/103] nfsd4: simplify distinguishing lock & open stateid's The trick free_stateid is using is a little cheesy, and we'll have more uses for this field later. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 9 +++------ fs/nfsd/state.h | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d2b637b717c3..7de214b860db 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2285,6 +2285,7 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open * list_add(&stp->st_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perstateowner, &sop->so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); + stp->st_type = NFS4_OPEN_STID; stp->st_stateowner = sop; get_nfs4_file(fp); stp->st_file = fp; @@ -3201,11 +3202,6 @@ static int is_delegation_stateid(stateid_t *stateid) return stateid->si_fileid == 0; } -static int is_open_stateid(struct nfs4_stateid *stateid) -{ - return stateid->st_openstp == NULL; -} - __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) { struct nfs4_stateid *stp = NULL; @@ -3369,7 +3365,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } } - if (is_open_stateid(stp)) { + if (stp->st_type == NFS4_OPEN_STID) { ret = nfserr_locks_held; goto out; } else { @@ -3928,6 +3924,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perstateowner, &sop->so_stateids); stp->st_stateowner = sop; + stp->st_type = NFS4_LOCK_STID; get_nfs4_file(fp); stp->st_file = fp; stp->st_stateid.si_boot = boot_time; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 6b706a60ce88..a06f55bd38b6 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -425,6 +425,9 @@ static inline struct file *find_any_file(struct nfs4_file *f) */ struct nfs4_stateid { +#define NFS4_OPEN_STID 1 +#define NFS4_LOCK_STID 2 + char st_type; struct list_head st_hash; struct list_head st_perfile; struct list_head st_perstateowner; From b79abaddfe7ef29e00d71721cf03a33e00d53317 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 22 Aug 2011 18:01:43 -0400 Subject: [PATCH 033/103] nfsd4: consolidate lock & open stateid tables There's no reason to have two separate hash tables for open and lock stateid's. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 63 +++++++++++---------------------------------- 1 file changed, 15 insertions(+), 48 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 7de214b860db..0198328c0e84 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -61,7 +61,6 @@ static u64 current_sessionid = 1; /* forward declarations */ static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); -static struct nfs4_stateid * search_for_stateid(stateid_t *stid); static struct nfs4_delegation * search_for_delegation(stateid_t *stid); static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); static int check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner); @@ -3211,7 +3210,7 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) goto out; status = nfserr_expired; - stp = search_for_stateid(stateid); + stp = find_stateid(stateid, 0); if (!stp) goto out; status = nfserr_bad_stateid; @@ -3349,7 +3348,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - stp = search_for_stateid(stateid); + stp = find_stateid(stateid, 0); if (!stp) { ret = nfserr_bad_stateid; goto out; @@ -3718,7 +3717,6 @@ lock_ownerstr_hashval(struct inode *inode, u32 cl_id, static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE]; static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; -static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; static int same_stateid(stateid_t *id_one, stateid_t *id_two) @@ -3728,50 +3726,21 @@ same_stateid(stateid_t *id_one, stateid_t *id_two) return id_one->si_fileid == id_two->si_fileid; } -static struct nfs4_stateid * -find_stateid(stateid_t *stid, int flags) +static struct nfs4_stateid *find_stateid(stateid_t *t, int flags) { - struct nfs4_stateid *local; - u32 st_id = stid->si_stateownerid; - u32 f_id = stid->si_fileid; + struct nfs4_stateid *s; unsigned int hashval; - dprintk("NFSD: find_stateid flags 0x%x\n",flags); - if (flags & (LOCK_STATE | RD_STATE | WR_STATE)) { - hashval = stateid_hashval(st_id, f_id); - list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) { - if ((local->st_stateid.si_stateownerid == st_id) && - (local->st_stateid.si_fileid == f_id)) - return local; + hashval = stateid_hashval(t->si_stateownerid, t->si_fileid); + list_for_each_entry(s, &stateid_hashtbl[hashval], st_hash) { + if (!same_stateid(&s->st_stateid, t)) + continue; + if (flags & LOCK_STATE && s->st_type != NFS4_LOCK_STID) + return NULL; + if (flags & OPEN_STATE && s->st_type != NFS4_OPEN_STID) + return NULL; + return s; } - } - - if (flags & (OPEN_STATE | RD_STATE | WR_STATE)) { - hashval = stateid_hashval(st_id, f_id); - list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) { - if ((local->st_stateid.si_stateownerid == st_id) && - (local->st_stateid.si_fileid == f_id)) - return local; - } - } - return NULL; -} - -static struct nfs4_stateid * -search_for_stateid(stateid_t *stid) -{ - struct nfs4_stateid *local; - unsigned int hashval = stateid_hashval(stid->si_stateownerid, stid->si_fileid); - - list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) { - if (same_stateid(&local->st_stateid, stid)) - return local; - } - - list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) { - if (same_stateid(&local->st_stateid, stid)) - return local; - } return NULL; } @@ -3920,7 +3889,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc INIT_LIST_HEAD(&stp->st_perfile); INIT_LIST_HEAD(&stp->st_perstateowner); INIT_LIST_HEAD(&stp->st_lockowners); /* not used */ - list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]); + list_add(&stp->st_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perstateowner, &sop->so_stateids); stp->st_stateowner = sop; @@ -4497,10 +4466,8 @@ nfs4_state_init(void) INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]); INIT_LIST_HEAD(&open_ownerid_hashtbl[i]); } - for (i = 0; i < STATEID_HASH_SIZE; i++) { + for (i = 0; i < STATEID_HASH_SIZE; i++) INIT_LIST_HEAD(&stateid_hashtbl[i]); - INIT_LIST_HEAD(&lockstateid_hashtbl[i]); - } for (i = 0; i < LOCK_HASH_SIZE; i++) { INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]); INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); From 81b829655d418316f0707b3656b45cff7a1dbf12 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 23 Aug 2011 11:03:29 -0400 Subject: [PATCH 034/103] nfsd4: simplify stateid generation code, fix wraparound Follow the recommendation from rfc3530bis for stateid generation number wraparound, simplify some code, and fix or remove incorrect comments. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 52 ++++++++++++++++++++------------------------- fs/nfsd/state.h | 3 +++ 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0198328c0e84..106e8fa63cdf 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3168,6 +3168,12 @@ grace_disallows_io(struct inode *inode) return locks_in_grace() && mandatory_lock(inode); } +/* Returns true iff a is later than b: */ +static bool stateid_generation_after(stateid_t *a, stateid_t *b) +{ + return (s32)a->si_generation - (s32)b->si_generation > 0; +} + static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session) { /* @@ -3175,25 +3181,25 @@ static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_sess * when it is zero. */ if (has_session && in->si_generation == 0) - goto out; + return nfs_ok; + + if (in->si_generation == ref->si_generation) + return nfs_ok; /* If the client sends us a stateid from the future, it's buggy: */ - if (in->si_generation > ref->si_generation) + if (stateid_generation_after(in, ref)) return nfserr_bad_stateid; /* - * The following, however, can happen. For example, if the - * client sends an open and some IO at the same time, the open - * may bump si_generation while the IO is still in flight. - * Thanks to hard links and renames, the client never knows what - * file an open will affect. So it could avoid that situation - * only by serializing all opens and IO from the same open - * owner. To recover from the old_stateid error, the client - * will just have to retry the IO: + * However, we could see a stateid from the past, even from a + * non-buggy client. For example, if the client sends a lock + * while some IO is outstanding, the lock may bump si_generation + * while the IO is still in flight. The client could avoid that + * situation by waiting for responses on all the IO requests, + * but better performance may result in retrying IO that + * receives an old_stateid error if requests are rarely + * reordered in flight: */ - if (in->si_generation < ref->si_generation) - return nfserr_old_stateid; -out: - return nfs_ok; + return nfserr_old_stateid; } static int is_delegation_stateid(stateid_t *stateid) @@ -3353,16 +3359,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ret = nfserr_bad_stateid; goto out; } - if (stateid->si_generation != 0) { - if (stateid->si_generation < stp->st_stateid.si_generation) { - ret = nfserr_old_stateid; - goto out; - } - if (stateid->si_generation > stp->st_stateid.si_generation) { - ret = nfserr_bad_stateid; - goto out; - } - } + ret = check_stateid_generation(stateid, &stp->st_stateid, 1); + if (ret) + goto out; if (stp->st_type == NFS4_OPEN_STID) { ret = nfserr_locks_held; @@ -3439,11 +3438,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, return nfserr_bad_stateid; } - /* - * We now validate the seqid and stateid generation numbers. - * For the moment, we ignore the possibility of - * generation number wraparound. - */ if (!nfsd4_has_session(cstate) && seqid != sop->so_seqid) goto check_replay; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index a06f55bd38b6..c425717715f6 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -293,6 +293,9 @@ static inline void update_stateid(stateid_t *stateid) { stateid->si_generation++; + /* Wraparound recommendation from 3530bis-13 9.1.3.2: */ + if (stateid->si_generation == 0) + stateid->si_generation = 1; } /* A reasonable value for REPLAY_ISIZE was estimated as follows: From 73997dc4183c580278ea8cb41c7a9655940801e0 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 31 Aug 2011 15:47:21 -0400 Subject: [PATCH 035/103] nfsd4: make delegation stateid's seqid start at 1 Thanks to Casey for reminding me that 5661 gives a special meaning to a value of 0 in the stateid's seqid field, so all stateid's should start out with si_generation 1. We were doing that in the open and lock cases for minorversion 1, but not for the delegation stateid, and not for openstateid's with v4.0. It doesn't *really* matter much for v4.0 or for delegation stateid's (which never get the seqid field incremented), but we may as well do the same for all of them. Reported-by: Casey Bodley Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 106e8fa63cdf..80af79ee5d51 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -250,7 +250,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f dp->dl_stateid.si_boot = boot_time; dp->dl_stateid.si_stateownerid = current_delegid++; dp->dl_stateid.si_fileid = 0; - dp->dl_stateid.si_generation = 0; + dp->dl_stateid.si_generation = 1; fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); @@ -2291,6 +2291,7 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open * stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; + /* note will be incremented before first return to client: */ stp->st_stateid.si_generation = 0; stp->st_access_bmap = 0; stp->st_deny_bmap = 0; @@ -2894,7 +2895,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open); if (status) goto out; - update_stateid(&stp->st_stateid); } else { status = nfs4_new_open(rqstp, &stp, fp, current_fh, open); if (status) @@ -2905,9 +2905,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf release_open_stateid(stp); goto out; } - if (nfsd4_has_session(&resp->cstate)) - update_stateid(&stp->st_stateid); } + update_stateid(&stp->st_stateid); memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); if (nfsd4_has_session(&resp->cstate)) @@ -3893,6 +3892,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; + /* note will be incremented before first return to client: */ stp->st_stateid.si_generation = 0; stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; From f3e4223751392b9bc0195a806a6e99b4cc399ac0 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 24 Aug 2011 12:27:31 -0400 Subject: [PATCH 036/103] nfsd4: centralize handling of replay owners Set the stateowner associated with a replay in one spot in nfs4_preprocess_seqid_op() and keep it in cstate. This allows removing a few lines of boilerplate from all the nfs4_preprocess_seqid_op() callers. Also turn ENCODE_SEQID_OP_TAIL into a function while we're here. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 25 ++++--------------------- fs/nfsd/nfs4xdr.c | 34 +++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 80af79ee5d51..e4535ff92876 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3425,12 +3425,15 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, /* It's not stale; let's assume it's expired: */ if (sop == NULL) return nfserr_expired; - *sopp = sop; + nfs4_get_stateowner(sop); + cstate->replay_owner = sop; goto check_replay; } *stpp = stp; *sopp = sop = stp->st_stateowner; + nfs4_get_stateowner(sop); + cstate->replay_owner = sop; if (nfs4_check_fh(current_fh, stp)) { dprintk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n"); @@ -3501,10 +3504,6 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_create_clid_dir(sop->so_client); out: - if (oc->oc_stateowner) { - nfs4_get_stateowner(oc->oc_stateowner); - cstate->replay_owner = oc->oc_stateowner; - } nfs4_unlock_state(); return status; } @@ -3574,10 +3573,6 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t)); status = nfs_ok; out: - if (od->od_stateowner) { - nfs4_get_stateowner(od->od_stateowner); - cstate->replay_owner = od->od_stateowner; - } nfs4_unlock_state(); return status; } @@ -3618,10 +3613,6 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (list_empty(&close->cl_stateowner->so_stateids)) move_to_close_lru(close->cl_stateowner); out: - if (close->cl_stateowner) { - nfs4_get_stateowner(close->cl_stateowner); - cstate->replay_owner = close->cl_stateowner; - } nfs4_unlock_state(); return status; } @@ -4086,10 +4077,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, out: if (status && lock->lk_is_new && lock_sop) release_lockowner(lock_sop); - if (lock->lk_replay_owner) { - nfs4_get_stateowner(lock->lk_replay_owner); - cstate->replay_owner = lock->lk_replay_owner; - } nfs4_unlock_state(); return status; } @@ -4244,10 +4231,6 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, memcpy(&locku->lu_stateid, &stp->st_stateid, sizeof(stateid_t)); out: - if (locku->lu_stateowner) { - nfs4_get_stateowner(locku->lu_stateowner); - cstate->replay_owner = locku->lu_stateowner; - } nfs4_unlock_state(); return status; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index f982d8574d95..ee1267838719 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1630,15 +1630,19 @@ static void write_cinfo(__be32 **p, struct nfsd4_change_info *c) * we know whether the error to be returned is a sequence id mutating error. */ -#define ENCODE_SEQID_OP_TAIL(stateowner) do { \ - if (seqid_mutating_err(ntohl(nfserr)) && stateowner) { \ - stateowner->so_seqid++; \ - stateowner->so_replay.rp_status = nfserr; \ - stateowner->so_replay.rp_buflen = \ - (((char *)(resp)->p - (char *)save)); \ - memcpy(stateowner->so_replay.rp_buf, save, \ - stateowner->so_replay.rp_buflen); \ - } } while (0); +static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, __be32 nfserr) +{ + struct nfs4_stateowner *stateowner = resp->cstate.replay_owner; + + if (seqid_mutating_err(ntohl(nfserr)) && stateowner) { + stateowner->so_seqid++; + stateowner->so_replay.rp_status = nfserr; + stateowner->so_replay.rp_buflen = + (char *)resp->p - (char *)save; + memcpy(stateowner->so_replay.rp_buf, save, + stateowner->so_replay.rp_buflen); + } +} /* Encode as an array of strings the string given with components * separated @sep. @@ -2495,7 +2499,7 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c if (!nfserr) nfsd4_encode_stateid(resp, &close->cl_stateid); - ENCODE_SEQID_OP_TAIL(close->cl_stateowner); + encode_seqid_op_tail(resp, save, nfserr); return nfserr; } @@ -2599,7 +2603,7 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lo else if (nfserr == nfserr_denied) nfsd4_encode_lock_denied(resp, &lock->lk_denied); - ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner); + encode_seqid_op_tail(resp, save, nfserr); return nfserr; } @@ -2619,7 +2623,7 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l if (!nfserr) nfsd4_encode_stateid(resp, &locku->lu_stateid); - ENCODE_SEQID_OP_TAIL(locku->lu_stateowner); + encode_seqid_op_tail(resp, save, nfserr); return nfserr; } @@ -2700,7 +2704,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op } /* XXX save filehandle here */ out: - ENCODE_SEQID_OP_TAIL(open->op_stateowner); + encode_seqid_op_tail(resp, save, nfserr); return nfserr; } @@ -2712,7 +2716,7 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct if (!nfserr) nfsd4_encode_stateid(resp, &oc->oc_resp_stateid); - ENCODE_SEQID_OP_TAIL(oc->oc_stateowner); + encode_seqid_op_tail(resp, save, nfserr); return nfserr; } @@ -2724,7 +2728,7 @@ nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struc if (!nfserr) nfsd4_encode_stateid(resp, &od->od_stateid); - ENCODE_SEQID_OP_TAIL(od->od_stateowner); + encode_seqid_op_tail(resp, save, nfserr); return nfserr; } From 9072d5c66b17292e3cd055bc8057b2ce6af2fe34 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 24 Aug 2011 12:45:03 -0400 Subject: [PATCH 037/103] nfsd4: cleanup seqid op stateowner usage Now that the replay owner is in the cstate we can remove it from a lot of other individual operations and further simplify nfs4_preprocess_seqid_op(). Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 57 +++++++++++++++++++-------------------------- fs/nfsd/nfs4xdr.c | 5 ---- fs/nfsd/xdr4.h | 7 ------ 3 files changed, 24 insertions(+), 45 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e4535ff92876..bc1a9dbc289c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3388,7 +3388,6 @@ setlkflg (int type) static __be32 nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, int flags, - struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp) { struct nfs4_stateid *stp; @@ -3400,7 +3399,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, seqid, STATEID_VAL(stateid)); *stpp = NULL; - *sopp = NULL; if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) { dprintk("NFSD: preprocess_seqid_op: magic stateid!\n"); @@ -3431,7 +3429,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, } *stpp = stp; - *sopp = sop = stp->st_stateowner; + sop = stp->st_stateowner; nfs4_get_stateowner(sop); cstate->replay_owner = sop; @@ -3467,7 +3465,6 @@ check_replay: } dprintk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d)\n", sop->so_seqid, seqid); - *sopp = NULL; return nfserr_bad_seqid; } @@ -3489,13 +3486,13 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); - if ((status = nfs4_preprocess_seqid_op(cstate, + status = nfs4_preprocess_seqid_op(cstate, oc->oc_seqid, &oc->oc_req_stateid, - CONFIRM | OPEN_STATE, - &oc->oc_stateowner, &stp))) + CONFIRM | OPEN_STATE, &stp); + if (status) goto out; - sop = oc->oc_stateowner; + sop = stp->st_stateowner; sop->so_confirmed = 1; update_stateid(&stp->st_stateid); memcpy(&oc->oc_resp_stateid, &stp->st_stateid, sizeof(stateid_t)); @@ -3547,11 +3544,9 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, return nfserr_inval; nfs4_lock_state(); - if ((status = nfs4_preprocess_seqid_op(cstate, - od->od_seqid, - &od->od_stateid, - OPEN_STATE, - &od->od_stateowner, &stp))) + status = nfs4_preprocess_seqid_op(cstate, od->od_seqid, + &od->od_stateid, OPEN_STATE, &stp); + if (status) goto out; status = nfserr_inval; @@ -3586,6 +3581,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; struct nfs4_stateid *stp; + struct nfs4_stateowner *so; dprintk("NFSD: nfsd4_close on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3593,12 +3589,12 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); /* check close_lru for replay */ - if ((status = nfs4_preprocess_seqid_op(cstate, - close->cl_seqid, + status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid, &close->cl_stateid, - OPEN_STATE | CLOSE_STATE, - &close->cl_stateowner, &stp))) + OPEN_STATE | CLOSE_STATE, &stp); + if (status) goto out; + so = stp->st_stateowner; status = nfs_ok; update_stateid(&stp->st_stateid); memcpy(&close->cl_stateid, &stp->st_stateid, sizeof(stateid_t)); @@ -3610,8 +3606,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * released by the laundromat service after the lease period * to enable us to handle CLOSE replay */ - if (list_empty(&close->cl_stateowner->so_stateids)) - move_to_close_lru(close->cl_stateowner); + if (list_empty(&so->so_stateids)) + move_to_close_lru(so); out: nfs4_unlock_state(); return status; @@ -3962,12 +3958,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, lock->lk_new_open_seqid, &lock->lk_new_open_stateid, - OPEN_STATE, - &lock->lk_replay_owner, &open_stp); + OPEN_STATE, &open_stp); if (status) goto out; status = nfserr_bad_stateid; - open_sop = lock->lk_replay_owner; + open_sop = open_stp->st_stateowner; if (!nfsd4_has_session(cstate) && !same_clid(&open_sop->so_client->cl_clientid, &lock->v.new.clientid)) @@ -3993,14 +3988,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, lock->lk_old_lock_seqid, &lock->lk_old_lock_stateid, - LOCK_STATE, - &lock->lk_replay_owner, &lock_stp); + LOCK_STATE, &lock_stp); if (status) goto out; - lock_sop = lock->lk_replay_owner; + lock_sop = lock_stp->st_stateowner; fp = lock_stp->st_file; } - /* lock->lk_replay_owner and lock_stp have been created or found */ + /* lock_sop and lock_stp have been created or found */ lkflg = setlkflg(lock->lk_type); status = nfs4_check_openmode(lock_stp, lkflg); @@ -4191,13 +4185,10 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); - if ((status = nfs4_preprocess_seqid_op(cstate, - locku->lu_seqid, - &locku->lu_stateid, - LOCK_STATE, - &locku->lu_stateowner, &stp))) + status = nfs4_preprocess_seqid_op(cstate, locku->lu_seqid, + &locku->lu_stateid, LOCK_STATE, &stp); + if (status) goto out; - filp = find_any_file(stp->st_file); if (!filp) { status = nfserr_lock_range; @@ -4206,7 +4197,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, BUG_ON(!filp); locks_init_lock(&file_lock); file_lock.fl_type = F_UNLCK; - file_lock.fl_owner = (fl_owner_t) locku->lu_stateowner; + file_lock.fl_owner = (fl_owner_t) stp->st_stateowner; file_lock.fl_pid = current->tgid; file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index ee1267838719..462c6eff8471 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -456,7 +456,6 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close) { DECODE_HEAD; - close->cl_stateowner = NULL; READ_BUF(4); READ32(close->cl_seqid); return nfsd4_decode_stateid(argp, &close->cl_stateid); @@ -551,7 +550,6 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) { DECODE_HEAD; - lock->lk_replay_owner = NULL; /* * type, reclaim(boolean), offset, length, new_lock_owner(boolean) */ @@ -611,7 +609,6 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku) { DECODE_HEAD; - locku->lu_stateowner = NULL; READ_BUF(8); READ32(locku->lu_type); if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT)) @@ -739,7 +736,6 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con { DECODE_HEAD; - open_conf->oc_stateowner = NULL; status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid); if (status) return status; @@ -754,7 +750,6 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d { DECODE_HEAD; - open_down->od_stateowner = NULL; status = nfsd4_decode_stateid(argp, &open_down->od_stateid); if (status) return status; diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 663193b21a24..341f0a17d217 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -81,7 +81,6 @@ struct nfsd4_access { struct nfsd4_close { u32 cl_seqid; /* request */ stateid_t cl_stateid; /* request+response */ - struct nfs4_stateowner * cl_stateowner; /* response */ }; struct nfsd4_commit { @@ -165,9 +164,6 @@ struct nfsd4_lock { } ok; struct nfsd4_lock_denied denied; } u; - /* The lk_replay_owner is the open owner in the open_to_lock_owner - * case and the lock owner otherwise: */ - struct nfs4_stateowner *lk_replay_owner; }; #define lk_new_open_seqid v.new.open_seqid #define lk_new_open_stateid v.new.open_stateid @@ -199,7 +195,6 @@ struct nfsd4_locku { stateid_t lu_stateid; u64 lu_offset; u64 lu_length; - struct nfs4_stateowner *lu_stateowner; }; @@ -243,7 +238,6 @@ struct nfsd4_open_confirm { stateid_t oc_req_stateid /* request */; u32 oc_seqid /* request */; stateid_t oc_resp_stateid /* response */; - struct nfs4_stateowner * oc_stateowner; /* response */ }; struct nfsd4_open_downgrade { @@ -251,7 +245,6 @@ struct nfsd4_open_downgrade { u32 od_seqid; u32 od_share_access; u32 od_share_deny; - struct nfs4_stateowner *od_stateowner; }; From 5ec094c1096ab3bb795651855d53f18daa26afde Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 30 Aug 2011 17:02:48 -0400 Subject: [PATCH 038/103] nfsd4: extend state lock over seqid replay logic There are currently a couple races in the seqid replay code: a retransmission could come while we're still encoding the original reply, or a new seqid-mutating call could come as we're encoding a replay. So, extend the state lock over the encoding (both encoding of a replayed reply and caching of the original encoded reply). I really hate doing this, and previously added the stateowner reference-counting code to avoid it (which was insufficient)--but I don't see a less complicated alternative at the moment. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 5 +++-- fs/nfsd/nfs4state.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 50bae7471147..50063a85f505 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -408,8 +408,8 @@ out: if (open->op_stateowner) { nfs4_get_stateowner(open->op_stateowner); cstate->replay_owner = open->op_stateowner; - } - nfs4_unlock_state(); + } else + nfs4_unlock_state(); return status; } @@ -1227,6 +1227,7 @@ encode_op: be32_to_cpu(status)); if (cstate->replay_owner) { + nfs4_unlock_state(); nfs4_put_stateowner(cstate->replay_owner); cstate->replay_owner = NULL; } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index bc1a9dbc289c..6cf729a096c3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3501,7 +3501,8 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_create_clid_dir(sop->so_client); out: - nfs4_unlock_state(); + if (!cstate->replay_owner) + nfs4_unlock_state(); return status; } @@ -3568,7 +3569,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t)); status = nfs_ok; out: - nfs4_unlock_state(); + if (!cstate->replay_owner) + nfs4_unlock_state(); return status; } @@ -3609,7 +3611,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (list_empty(&so->so_stateids)) move_to_close_lru(so); out: - nfs4_unlock_state(); + if (!cstate->replay_owner) + nfs4_unlock_state(); return status; } @@ -4071,7 +4074,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, out: if (status && lock->lk_is_new && lock_sop) release_lockowner(lock_sop); - nfs4_unlock_state(); + if (!cstate->replay_owner) + nfs4_unlock_state(); return status; } From fff6ca9cc46857e5814cf687e5fb1b8a876766a4 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 25 Aug 2011 18:17:52 -0400 Subject: [PATCH 039/103] nfsd4: eliminate impossible open replay case If open fails with any error other than nfserr_replay_me, then the main nfsd4_proc_compound() loop continues unconditionally to nfsd4_encode_operation(), which will always call encode_seqid_op_tail. Thus the condition we check for here does not occur. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6cf729a096c3..26b0c75aa93b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2495,18 +2495,8 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, open->op_stateowner = NULL; goto renew; } - if (open->op_seqid == sop->so_seqid - 1) { - if (sop->so_replay.rp_buflen) - return nfserr_replay_me; - /* The original OPEN failed so spectacularly - * that we don't even have replay data saved! - * Therefore, we have no choice but to continue - * processing this OPEN; presumably, we'll - * fail again for the same reason. - */ - dprintk("nfsd4_process_open1: replay with no replay cache\n"); - goto renew; - } + if (open->op_seqid == sop->so_seqid - 1) + return nfserr_replay_me; if (open->op_seqid != sop->so_seqid) return nfserr_bad_seqid; renew: From 7c13f344cf8bec22301c5ed7ef1d90eecb57ba43 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 30 Aug 2011 22:15:47 -0400 Subject: [PATCH 040/103] nfsd4: drop most stateowner refcounting Maybe we'll bring it back some day, but we don't have much real use for it now. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 6 ++---- fs/nfsd/nfs4state.c | 23 ++++++++++++----------- fs/nfsd/nfs4xdr.c | 11 ++++++----- fs/nfsd/state.h | 15 +-------------- fs/nfsd/xdr4.h | 2 +- 5 files changed, 22 insertions(+), 35 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 50063a85f505..ce151f0ed4b9 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -405,10 +405,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, */ status = nfsd4_process_open2(rqstp, &cstate->current_fh, open); out: - if (open->op_stateowner) { - nfs4_get_stateowner(open->op_stateowner); + if (open->op_stateowner) cstate->replay_owner = open->op_stateowner; - } else + else nfs4_unlock_state(); return status; } @@ -1228,7 +1227,6 @@ encode_op: if (cstate->replay_owner) { nfs4_unlock_state(); - nfs4_put_stateowner(cstate->replay_owner); cstate->replay_owner = NULL; } /* XXX Ugh, we need to get rid of this kind of special case: */ diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 26b0c75aa93b..834a5f844f42 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -453,7 +453,7 @@ static void unhash_lockowner(struct nfs4_stateowner *sop) static void release_lockowner(struct nfs4_stateowner *sop) { unhash_lockowner(sop); - nfs4_put_stateowner(sop); + nfs4_free_stateowner(sop); } static void @@ -496,7 +496,7 @@ static void release_openowner(struct nfs4_stateowner *sop) { unhash_openowner(sop); list_del(&sop->so_close_lru); - nfs4_put_stateowner(sop); + nfs4_free_stateowner(sop); } #define SESSION_HASH_SIZE 512 @@ -2206,10 +2206,8 @@ out_nomem: } void -nfs4_free_stateowner(struct kref *kref) +nfs4_free_stateowner(struct nfs4_stateowner *sop) { - struct nfs4_stateowner *sop = - container_of(kref, struct nfs4_stateowner, so_ref); kfree(sop->so_owner.data); kmem_cache_free(stateowner_slab, sop); } @@ -2236,7 +2234,6 @@ static inline struct nfs4_stateowner *alloc_stateowner(struct xdr_netobj *owner, } sop->so_owner.len = owner->len; - kref_init(&sop->so_ref); INIT_LIST_HEAD(&sop->so_perclient); INIT_LIST_HEAD(&sop->so_stateids); INIT_LIST_HEAD(&sop->so_perstateid); @@ -3413,14 +3410,12 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, /* It's not stale; let's assume it's expired: */ if (sop == NULL) return nfserr_expired; - nfs4_get_stateowner(sop); cstate->replay_owner = sop; goto check_replay; } *stpp = stp; sop = stp->st_stateowner; - nfs4_get_stateowner(sop); cstate->replay_owner = sop; if (nfs4_check_fh(current_fh, stp)) { @@ -3783,11 +3778,17 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) if (fl->fl_lmops == &nfsd_posix_mng_ops) { sop = (struct nfs4_stateowner *) fl->fl_owner; - kref_get(&sop->so_ref); - deny->ld_sop = sop; + deny->ld_owner.data = kmemdup(sop->so_owner.data, + sop->so_owner.len, GFP_KERNEL); + if (!deny->ld_owner.data) + /* We just don't care that much */ + goto nevermind; + deny->ld_owner.len = sop->so_owner.len; deny->ld_clientid = sop->so_client->cl_clientid; } else { - deny->ld_sop = NULL; +nevermind: + deny->ld_owner.len = 0; + deny->ld_owner.data = NULL; deny->ld_clientid.cl_boot = 0; deny->ld_clientid.cl_id = 0; } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 462c6eff8471..c4dcba3aac1f 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2570,17 +2570,18 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh static void nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld) { + struct xdr_netobj *conf = &ld->ld_owner; __be32 *p; - RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0)); + RESERVE_SPACE(32 + XDR_LEN(conf->len)); WRITE64(ld->ld_start); WRITE64(ld->ld_length); WRITE32(ld->ld_type); - if (ld->ld_sop) { + if (conf->len) { WRITEMEM(&ld->ld_clientid, 8); - WRITE32(ld->ld_sop->so_owner.len); - WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len); - kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner); + WRITE32(conf->len); + WRITEMEM(conf->data, conf->len); + kfree(conf->data); } else { /* non - nfsv4 lock in conflict, no clientid nor owner */ WRITE64((u64)0); /* clientid */ WRITE32(0); /* length of owner name */ diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index c425717715f6..f7114fc21dee 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -338,7 +338,6 @@ struct nfs4_replay { * reaped by laundramat thread after lease period. */ struct nfs4_stateowner { - struct kref so_ref; struct list_head so_idhash; /* hash by so_id */ struct list_head so_strhash; /* hash by op_name */ struct list_head so_perclient; @@ -459,7 +458,7 @@ extern void nfs4_lock_state(void); extern void nfs4_unlock_state(void); extern int nfs4_in_grace(void); extern __be32 nfs4_check_open_reclaim(clientid_t *clid); -extern void nfs4_free_stateowner(struct kref *kref); +extern void nfs4_free_stateowner(struct nfs4_stateowner *sop); extern int set_callback_cred(void); extern void nfsd4_probe_callback(struct nfs4_client *clp); extern void nfsd4_probe_callback_sync(struct nfs4_client *clp); @@ -482,16 +481,4 @@ extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); extern void release_session_client(struct nfsd4_session *); extern __be32 nfs4_validate_stateid(stateid_t *, bool); -static inline void -nfs4_put_stateowner(struct nfs4_stateowner *so) -{ - kref_put(&so->so_ref, nfs4_free_stateowner); -} - -static inline void -nfs4_get_stateowner(struct nfs4_stateowner *so) -{ - kref_get(&so->so_ref); -} - #endif /* NFSD4_STATE_H */ diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 341f0a17d217..de236fb89e74 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -130,7 +130,7 @@ struct nfsd4_link { struct nfsd4_lock_denied { clientid_t ld_clientid; - struct nfs4_stateowner *ld_sop; + struct xdr_netobj ld_owner; u64 ld_start; u64 ld_length; u32 ld_type; From 16d259418b7c0dda79b71bfbfeaedc0ba4035f23 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 1 Sep 2011 11:31:45 -0400 Subject: [PATCH 041/103] nfsd4: eliminate unused lt_stateowner This is used only as a local variable. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 ++++---- fs/nfsd/xdr4.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 834a5f844f42..a47bf884726f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4098,6 +4098,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct inode *inode; struct file_lock file_lock; + struct nfs4_stateowner *so; int error; __be32 status; @@ -4107,7 +4108,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (check_lock_length(lockt->lt_offset, lockt->lt_length)) return nfserr_inval; - lockt->lt_stateowner = NULL; nfs4_lock_state(); status = nfserr_stale_clientid; @@ -4134,10 +4134,10 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - lockt->lt_stateowner = find_lockstateowner_str(inode, + so = find_lockstateowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner); - if (lockt->lt_stateowner) - file_lock.fl_owner = (fl_owner_t)lockt->lt_stateowner; + if (so) + file_lock.fl_owner = (fl_owner_t)so; file_lock.fl_pid = current->tgid; file_lock.fl_flags = FL_POSIX; diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index de236fb89e74..27a3dfab96a9 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -184,7 +184,6 @@ struct nfsd4_lockt { struct xdr_netobj lt_owner; u64 lt_offset; u64 lt_length; - struct nfs4_stateowner * lt_stateowner; struct nfsd4_lock_denied lt_denied; }; From 7a8711c9a6e2299c324c153da6e3381b1fd56128 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 2 Sep 2011 09:03:37 -0400 Subject: [PATCH 042/103] nfsd4: share common seqid checks Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a47bf884726f..8edc9ad63ea6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2456,6 +2456,16 @@ static const struct lock_manager_operations nfsd_lease_mng_ops = { .lm_change = nfsd_change_deleg_cb, }; +static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4_stateowner *so, u32 seqid) +{ + if (nfsd4_has_session(cstate)) + return nfs_ok; + if (seqid == so->so_seqid - 1) + return nfserr_replay_me; + if (seqid == so->so_seqid) + return nfs_ok; + return nfserr_bad_seqid; +} __be32 nfsd4_process_open1(struct nfsd4_compound_state *cstate, @@ -2465,6 +2475,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, struct nfs4_client *clp = NULL; unsigned int strhashval; struct nfs4_stateowner *sop = NULL; + __be32 status; if (!check_name(open->op_owner)) return nfserr_inval; @@ -2482,9 +2493,6 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, return nfserr_expired; goto renew; } - /* When sessions are used, skip open sequenceid processing */ - if (nfsd4_has_session(cstate)) - goto renew; if (!sop->so_confirmed) { /* Replace unconfirmed owners without checking for replay. */ clp = sop->so_client; @@ -2492,10 +2500,9 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, open->op_stateowner = NULL; goto renew; } - if (open->op_seqid == sop->so_seqid - 1) - return nfserr_replay_me; - if (open->op_seqid != sop->so_seqid) - return nfserr_bad_seqid; + status = nfsd4_check_seqid(cstate, sop, open->op_seqid); + if (status) + return status; renew: if (open->op_stateowner == NULL) { sop = alloc_init_open_stateowner(strhashval, clp, open); @@ -3411,7 +3418,10 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, if (sop == NULL) return nfserr_expired; cstate->replay_owner = sop; - goto check_replay; + status = nfsd4_check_seqid(cstate, sop, seqid); + if (status) + return status; + return nfserr_bad_seqid; } *stpp = stp; @@ -3423,8 +3433,9 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, return nfserr_bad_stateid; } - if (!nfsd4_has_session(cstate) && seqid != sop->so_seqid) - goto check_replay; + status = nfsd4_check_seqid(cstate, sop, seqid); + if (status) + return status; if (sop->so_confirmed && flags & CONFIRM) { dprintk("NFSD: preprocess_seqid_op: expected" @@ -3441,16 +3452,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, return status; renew_client(sop->so_client); return nfs_ok; - -check_replay: - if (seqid == sop->so_seqid - 1) { - dprintk("NFSD: preprocess_seqid_op: retransmission?\n"); - /* indicate replay to calling function */ - return nfserr_replay_me; - } - dprintk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d)\n", - sop->so_seqid, seqid); - return nfserr_bad_seqid; } __be32 From 77eaae8d44ec5942033b751d0e0d2914c9411862 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 2 Sep 2011 12:08:20 -0400 Subject: [PATCH 043/103] nfsd4: simplify check_open logic Sometimes the single-exit style is good, sometimes it's unnecessarily convoluted.... Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8edc9ad63ea6..8694e60a4520 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2573,7 +2573,6 @@ static __be32 nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_stateid **stpp) { struct nfs4_stateid *local; - __be32 status = nfserr_share_denied; struct nfs4_stateowner *sop = open->op_stateowner; list_for_each_entry(local, &fp->fi_stateids, st_perfile) { @@ -2585,11 +2584,9 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_state *stpp = local; /* check for conflicting share reservations */ if (!test_share(local, open)) - goto out; + return nfserr_share_denied; } - status = 0; -out: - return status; + return nfs_ok; } static inline struct nfs4_stateid * From 68b66e8270f44d297a48662e6aed72c5944f77bd Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 2 Sep 2011 12:19:43 -0400 Subject: [PATCH 044/103] nfsd4: move double-confirm test to open_confirm I don't see the point of having this check in nfs4_preprocess_seqid_op() when it's only needed by the one caller. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8694e60a4520..9c44630a245a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3434,11 +3434,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, if (status) return status; - if (sop->so_confirmed && flags & CONFIRM) { - dprintk("NFSD: preprocess_seqid_op: expected" - " unconfirmed stateowner!\n"); - return nfserr_bad_stateid; - } if (!sop->so_confirmed && !(flags & CONFIRM)) { dprintk("NFSD: preprocess_seqid_op: stateowner not" " confirmed yet!\n"); @@ -3473,9 +3468,11 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, oc->oc_seqid, &oc->oc_req_stateid, CONFIRM | OPEN_STATE, &stp); if (status) - goto out; - + goto out; sop = stp->st_stateowner; + status = nfserr_bad_stateid; + if (sop->so_confirmed) + goto out; sop->so_confirmed = 1; update_stateid(&stp->st_stateid); memcpy(&oc->oc_resp_stateid, &stp->st_stateid, sizeof(stateid_t)); @@ -3483,6 +3480,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stateid)); nfsd4_create_clid_dir(sop->so_client); + status = nfs_ok; out: if (!cstate->replay_owner) nfs4_unlock_state(); From f4dee24cca98739a4190a00fa014cd1b7e2581a4 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 2 Sep 2011 16:36:49 -0400 Subject: [PATCH 045/103] nfsd4: move CLOSE_STATE special case to caller Move the CLOSE_STATE case into the unique caller that cares about it rather than putting it in preprocess_seqid_op. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 56 ++++++++++++++++++++++----------------------- fs/nfsd/state.h | 1 - 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9c44630a245a..eb11626babc6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3070,15 +3070,13 @@ laundromat_main(struct work_struct *not_used) } static struct nfs4_stateowner * -search_close_lru(u32 st_id, int flags) +search_close_lru(u32 st_id) { - struct nfs4_stateowner *local = NULL; + struct nfs4_stateowner *local; - if (flags & CLOSE_STATE) { - list_for_each_entry(local, &close_lru, so_close_lru) { - if (local->so_id == st_id) - return local; - } + list_for_each_entry(local, &close_lru, so_close_lru) { + if (local->so_id == st_id) + return local; } return NULL; } @@ -3381,7 +3379,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateid **stpp) { - struct nfs4_stateid *stp; struct nfs4_stateowner *sop; struct svc_fh *current_fh = &cstate->current_fh; __be32 status; @@ -3404,28 +3401,14 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, * the confirmed flag is incorrecly set, or the generation * number is incorrect. */ - stp = find_stateid(stateid, flags); - if (stp == NULL) { - /* - * Also, we should make sure this isn't just the result of - * a replayed close: - */ - sop = search_close_lru(stateid->si_stateownerid, flags); - /* It's not stale; let's assume it's expired: */ - if (sop == NULL) - return nfserr_expired; - cstate->replay_owner = sop; - status = nfsd4_check_seqid(cstate, sop, seqid); - if (status) - return status; - return nfserr_bad_seqid; - } + *stpp = find_stateid(stateid, flags); + if (*stpp == NULL) + return nfserr_expired; - *stpp = stp; - sop = stp->st_stateowner; + sop = (*stpp)->st_stateowner; cstate->replay_owner = sop; - if (nfs4_check_fh(current_fh, stp)) { + if (nfs4_check_fh(current_fh, *stpp)) { dprintk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n"); return nfserr_bad_stateid; } @@ -3439,7 +3422,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, " confirmed yet!\n"); return nfserr_bad_stateid; } - status = check_stateid_generation(stateid, &stp->st_stateid, nfsd4_has_session(cstate)); + status = check_stateid_generation(stateid, &(*stpp)->st_stateid, nfsd4_has_session(cstate)); if (status) return status; renew_client(sop->so_client); @@ -3574,7 +3557,22 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* check close_lru for replay */ status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid, &close->cl_stateid, - OPEN_STATE | CLOSE_STATE, &stp); + OPEN_STATE, &stp); + if (stp == NULL && status == nfserr_expired) { + /* + * Also, we should make sure this isn't just the result of + * a replayed close: + */ + so = search_close_lru(close->cl_stateid.si_stateownerid); + /* It's not stale; let's assume it's expired: */ + if (so == NULL) + goto out; + cstate->replay_owner = so; + status = nfsd4_check_seqid(cstate, so, close->cl_seqid); + if (status) + goto out; + status = nfserr_bad_seqid; + } if (status) goto out; so = stp->st_stateowner; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index f7114fc21dee..0d88000d15d7 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -448,7 +448,6 @@ struct nfs4_stateid { #define LOCK_STATE 0x00000008 #define RD_STATE 0x00000010 #define WR_STATE 0x00000020 -#define CLOSE_STATE 0x00000040 struct nfsd4_compound_state; From fe0750e5c43189adb6e6fc59837af7d5a588f413 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 30 Jul 2011 23:33:59 -0400 Subject: [PATCH 046/103] nfsd4: split stateowners into open and lockowners The stateowner has some fields that only make sense for openowners, and some that only make sense for lockowners, and I find it a lot clearer if those are separated out. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 18 +-- fs/nfsd/nfs4state.c | 367 ++++++++++++++++++++++---------------------- fs/nfsd/nfs4xdr.c | 2 +- fs/nfsd/state.h | 33 +++- fs/nfsd/xdr4.h | 2 +- 5 files changed, 224 insertions(+), 198 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index ce151f0ed4b9..460eeb329d81 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -250,7 +250,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o fh_dup2(current_fh, &resfh); /* set reply cache */ - fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh, + fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, &resfh.fh_handle); if (!created) status = do_open_permission(rqstp, current_fh, open, @@ -277,7 +277,7 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info)); /* set replay cache */ - fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh, + fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, ¤t_fh->fh_handle); open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && @@ -306,9 +306,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status; struct nfsd4_compoundres *resp; - dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n", + dprintk("NFSD: nfsd4_open filename %.*s op_openowner %p\n", (int)open->op_fname.len, open->op_fname.data, - open->op_stateowner); + open->op_openowner); /* This check required by spec. */ if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) @@ -332,7 +332,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, resp = rqstp->rq_resp; status = nfsd4_process_open1(&resp->cstate, open); if (status == nfserr_replay_me) { - struct nfs4_replay *rp = &open->op_stateowner->so_replay; + struct nfs4_replay *rp = &open->op_openowner->oo_owner.so_replay; fh_put(&cstate->current_fh); fh_copy_shallow(&cstate->current_fh.fh_handle, &rp->rp_openfh); @@ -374,7 +374,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; break; case NFS4_OPEN_CLAIM_PREVIOUS: - open->op_stateowner->so_confirmed = 1; + open->op_openowner->oo_confirmed = 1; /* * The CURRENT_FH is already set to the file being * opened. (1) set open->op_cinfo, (2) set @@ -387,7 +387,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; break; case NFS4_OPEN_CLAIM_DELEGATE_PREV: - open->op_stateowner->so_confirmed = 1; + open->op_openowner->oo_confirmed = 1; dprintk("NFSD: unsupported OPEN claim type %d\n", open->op_claim_type); status = nfserr_notsupp; @@ -405,8 +405,8 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, */ status = nfsd4_process_open2(rqstp, &cstate->current_fh, open); out: - if (open->op_stateowner) - cstate->replay_owner = open->op_stateowner; + if (open->op_openowner) + cstate->replay_owner = &open->op_openowner->oo_owner; else nfs4_unlock_state(); return status; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index eb11626babc6..567130dccda0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -63,7 +63,7 @@ static u64 current_sessionid = 1; static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); static struct nfs4_delegation * search_for_delegation(stateid_t *stid); static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); -static int check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner); +static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner); /* Locking: */ @@ -77,7 +77,8 @@ static DEFINE_MUTEX(client_mutex); */ static DEFINE_SPINLOCK(recall_lock); -static struct kmem_cache *stateowner_slab = NULL; +static struct kmem_cache *openowner_slab = NULL; +static struct kmem_cache *lockowner_slab = NULL; static struct kmem_cache *file_slab = NULL; static struct kmem_cache *stateid_slab = NULL; static struct kmem_cache *deleg_slab = NULL; @@ -432,41 +433,39 @@ static void release_lock_stateid(struct nfs4_stateid *stp) unhash_generic_stateid(stp); file = find_any_file(stp->st_file); if (file) - locks_remove_posix(file, (fl_owner_t)stp->st_stateowner); + locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner)); free_generic_stateid(stp); } -static void unhash_lockowner(struct nfs4_stateowner *sop) +static void unhash_lockowner(struct nfs4_lockowner *lo) { struct nfs4_stateid *stp; - list_del(&sop->so_idhash); - list_del(&sop->so_strhash); - list_del(&sop->so_perstateid); - while (!list_empty(&sop->so_stateids)) { - stp = list_first_entry(&sop->so_stateids, + list_del(&lo->lo_owner.so_idhash); + list_del(&lo->lo_owner.so_strhash); + list_del(&lo->lo_perstateid); + while (!list_empty(&lo->lo_owner.so_stateids)) { + stp = list_first_entry(&lo->lo_owner.so_stateids, struct nfs4_stateid, st_perstateowner); release_lock_stateid(stp); } } -static void release_lockowner(struct nfs4_stateowner *sop) +static void release_lockowner(struct nfs4_lockowner *lo) { - unhash_lockowner(sop); - nfs4_free_stateowner(sop); + unhash_lockowner(lo); + nfs4_free_lockowner(lo); } static void release_stateid_lockowners(struct nfs4_stateid *open_stp) { - struct nfs4_stateowner *lock_sop; + struct nfs4_lockowner *lo; while (!list_empty(&open_stp->st_lockowners)) { - lock_sop = list_entry(open_stp->st_lockowners.next, - struct nfs4_stateowner, so_perstateid); - /* list_del(&open_stp->st_lockowners); */ - BUG_ON(lock_sop->so_is_open_owner); - release_lockowner(lock_sop); + lo = list_entry(open_stp->st_lockowners.next, + struct nfs4_lockowner, lo_perstateid); + release_lockowner(lo); } } @@ -477,26 +476,25 @@ static void release_open_stateid(struct nfs4_stateid *stp) free_generic_stateid(stp); } -static void unhash_openowner(struct nfs4_stateowner *sop) +static void unhash_openowner(struct nfs4_openowner *oo) { struct nfs4_stateid *stp; - list_del(&sop->so_idhash); - list_del(&sop->so_strhash); - list_del(&sop->so_perclient); - list_del(&sop->so_perstateid); /* XXX: necessary? */ - while (!list_empty(&sop->so_stateids)) { - stp = list_first_entry(&sop->so_stateids, + list_del(&oo->oo_owner.so_idhash); + list_del(&oo->oo_owner.so_strhash); + list_del(&oo->oo_perclient); + while (!list_empty(&oo->oo_owner.so_stateids)) { + stp = list_first_entry(&oo->oo_owner.so_stateids, struct nfs4_stateid, st_perstateowner); release_open_stateid(stp); } } -static void release_openowner(struct nfs4_stateowner *sop) +static void release_openowner(struct nfs4_openowner *oo) { - unhash_openowner(sop); - list_del(&sop->so_close_lru); - nfs4_free_stateowner(sop); + unhash_openowner(oo); + list_del(&oo->oo_close_lru); + nfs4_free_openowner(oo); } #define SESSION_HASH_SIZE 512 @@ -961,7 +959,7 @@ unhash_client_locked(struct nfs4_client *clp) static void expire_client(struct nfs4_client *clp) { - struct nfs4_stateowner *sop; + struct nfs4_openowner *oo; struct nfs4_delegation *dp; struct list_head reaplist; @@ -979,8 +977,8 @@ expire_client(struct nfs4_client *clp) unhash_delegation(dp); } while (!list_empty(&clp->cl_openowners)) { - sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient); - release_openowner(sop); + oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient); + release_openowner(oo); } nfsd4_shutdown_callback(clp); if (clp->cl_cb_conn.cb_xprt) @@ -2173,7 +2171,8 @@ nfsd4_free_slab(struct kmem_cache **slab) void nfsd4_free_slabs(void) { - nfsd4_free_slab(&stateowner_slab); + nfsd4_free_slab(&openowner_slab); + nfsd4_free_slab(&lockowner_slab); nfsd4_free_slab(&file_slab); nfsd4_free_slab(&stateid_slab); nfsd4_free_slab(&deleg_slab); @@ -2182,9 +2181,13 @@ nfsd4_free_slabs(void) static int nfsd4_init_slabs(void) { - stateowner_slab = kmem_cache_create("nfsd4_stateowners", - sizeof(struct nfs4_stateowner), 0, 0, NULL); - if (stateowner_slab == NULL) + openowner_slab = kmem_cache_create("nfsd4_openowners", + sizeof(struct nfs4_openowner), 0, 0, NULL); + if (openowner_slab == NULL) + goto out_nomem; + lockowner_slab = kmem_cache_create("nfsd4_lockowners", + sizeof(struct nfs4_openowner), 0, 0, NULL); + if (lockowner_slab == NULL) goto out_nomem; file_slab = kmem_cache_create("nfsd4_files", sizeof(struct nfs4_file), 0, 0, NULL); @@ -2205,11 +2208,16 @@ out_nomem: return -ENOMEM; } -void -nfs4_free_stateowner(struct nfs4_stateowner *sop) +void nfs4_free_openowner(struct nfs4_openowner *oo) { - kfree(sop->so_owner.data); - kmem_cache_free(stateowner_slab, sop); + kfree(oo->oo_owner.so_owner.data); + kmem_cache_free(openowner_slab, oo); +} + +void nfs4_free_lockowner(struct nfs4_lockowner *lo) +{ + kfree(lo->lo_owner.so_owner.data); + kmem_cache_free(lockowner_slab, lo); } static void init_nfs4_replay(struct nfs4_replay *rp) @@ -2219,74 +2227,72 @@ static void init_nfs4_replay(struct nfs4_replay *rp) rp->rp_buf = rp->rp_ibuf; } -static inline struct nfs4_stateowner *alloc_stateowner(struct xdr_netobj *owner, struct nfs4_client *clp) +static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj *owner, struct nfs4_client *clp) { struct nfs4_stateowner *sop; - sop = kmem_cache_alloc(stateowner_slab, GFP_KERNEL); + sop = kmem_cache_alloc(slab, GFP_KERNEL); if (!sop) return NULL; sop->so_owner.data = kmemdup(owner->data, owner->len, GFP_KERNEL); if (!sop->so_owner.data) { - kmem_cache_free(stateowner_slab, sop); + kmem_cache_free(slab, sop); return NULL; } sop->so_owner.len = owner->len; - INIT_LIST_HEAD(&sop->so_perclient); INIT_LIST_HEAD(&sop->so_stateids); - INIT_LIST_HEAD(&sop->so_perstateid); - INIT_LIST_HEAD(&sop->so_close_lru); sop->so_id = current_ownerid++; - sop->so_time = 0; sop->so_client = clp; init_nfs4_replay(&sop->so_replay); return sop; } -static void hash_openowner(struct nfs4_stateowner *sop, struct nfs4_client *clp, unsigned int strhashval) +static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval) { unsigned int idhashval; - idhashval = open_ownerid_hashval(sop->so_id); - list_add(&sop->so_idhash, &open_ownerid_hashtbl[idhashval]); - list_add(&sop->so_strhash, &open_ownerstr_hashtbl[strhashval]); - list_add(&sop->so_perclient, &clp->cl_openowners); + idhashval = open_ownerid_hashval(oo->oo_owner.so_id); + list_add(&oo->oo_owner.so_idhash, &open_ownerid_hashtbl[idhashval]); + list_add(&oo->oo_owner.so_strhash, &open_ownerstr_hashtbl[strhashval]); + list_add(&oo->oo_perclient, &clp->cl_openowners); } -static struct nfs4_stateowner * +static struct nfs4_openowner * alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) { - struct nfs4_stateowner *sop; + struct nfs4_openowner *oo; - sop = alloc_stateowner(&open->op_owner, clp); - if (!sop) + oo = alloc_stateowner(openowner_slab, &open->op_owner, clp); + if (!oo) return NULL; - sop->so_is_open_owner = 1; - sop->so_seqid = open->op_seqid; - sop->so_confirmed = 0; - hash_openowner(sop, clp, strhashval); - return sop; + oo->oo_owner.so_is_open_owner = 1; + oo->oo_owner.so_seqid = open->op_seqid; + oo->oo_confirmed = 0; + oo->oo_time = 0; + INIT_LIST_HEAD(&oo->oo_close_lru); + hash_openowner(oo, clp, strhashval); + return oo; } static inline void init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { - struct nfs4_stateowner *sop = open->op_stateowner; - unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); + struct nfs4_openowner *oo = open->op_openowner; + unsigned int hashval = stateid_hashval(oo->oo_owner.so_id, fp->fi_id); INIT_LIST_HEAD(&stp->st_hash); INIT_LIST_HEAD(&stp->st_perstateowner); INIT_LIST_HEAD(&stp->st_lockowners); INIT_LIST_HEAD(&stp->st_perfile); list_add(&stp->st_hash, &stateid_hashtbl[hashval]); - list_add(&stp->st_perstateowner, &sop->so_stateids); + list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); stp->st_type = NFS4_OPEN_STID; - stp->st_stateowner = sop; + stp->st_stateowner = &oo->oo_owner; get_nfs4_file(fp); stp->st_file = fp; stp->st_stateid.si_boot = boot_time; - stp->st_stateid.si_stateownerid = sop->so_id; + stp->st_stateid.si_stateownerid = oo->oo_owner.so_id; stp->st_stateid.si_fileid = fp->fi_id; /* note will be incremented before first return to client: */ stp->st_stateid.si_generation = 0; @@ -2299,12 +2305,12 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open * } static void -move_to_close_lru(struct nfs4_stateowner *sop) +move_to_close_lru(struct nfs4_openowner *oo) { - dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); + dprintk("NFSD: move_to_close_lru nfs4_openowner %p\n", oo); - list_move_tail(&sop->so_close_lru, &close_lru); - sop->so_time = get_seconds(); + list_move_tail(&oo->oo_close_lru, &close_lru); + oo->oo_time = get_seconds(); } static int @@ -2316,14 +2322,14 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, (sop->so_client->cl_clientid.cl_id == clid->cl_id); } -static struct nfs4_stateowner * +static struct nfs4_openowner * find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open) { struct nfs4_stateowner *so = NULL; list_for_each_entry(so, &open_ownerstr_hashtbl[hashval], so_strhash) { if (same_owner_str(so, &open->op_owner, &open->op_clientid)) - return so; + return container_of(so, struct nfs4_openowner, oo_owner); } return NULL; } @@ -2474,7 +2480,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, clientid_t *clientid = &open->op_clientid; struct nfs4_client *clp = NULL; unsigned int strhashval; - struct nfs4_stateowner *sop = NULL; + struct nfs4_openowner *oo = NULL; __be32 status; if (!check_name(open->op_owner)) @@ -2484,34 +2490,34 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, return nfserr_stale_clientid; strhashval = open_ownerstr_hashval(clientid->cl_id, &open->op_owner); - sop = find_openstateowner_str(strhashval, open); - open->op_stateowner = sop; - if (!sop) { + oo = find_openstateowner_str(strhashval, open); + open->op_openowner = oo; + if (!oo) { /* Make sure the client's lease hasn't expired. */ clp = find_confirmed_client(clientid); if (clp == NULL) return nfserr_expired; goto renew; } - if (!sop->so_confirmed) { + if (!oo->oo_confirmed) { /* Replace unconfirmed owners without checking for replay. */ - clp = sop->so_client; - release_openowner(sop); - open->op_stateowner = NULL; + clp = oo->oo_owner.so_client; + release_openowner(oo); + open->op_openowner = NULL; goto renew; } - status = nfsd4_check_seqid(cstate, sop, open->op_seqid); + status = nfsd4_check_seqid(cstate, &oo->oo_owner, open->op_seqid); if (status) return status; renew: - if (open->op_stateowner == NULL) { - sop = alloc_init_open_stateowner(strhashval, clp, open); - if (sop == NULL) + if (open->op_openowner == NULL) { + oo = alloc_init_open_stateowner(strhashval, clp, open); + if (oo == NULL) return nfserr_jukebox; - open->op_stateowner = sop; + open->op_openowner = oo; } - list_del_init(&sop->so_close_lru); - renew_client(sop->so_client); + list_del_init(&oo->oo_close_lru); + renew_client(oo->oo_owner.so_client); return nfs_ok; } @@ -2565,7 +2571,7 @@ out: return nfs_ok; if (status) return status; - open->op_stateowner->so_confirmed = 1; + open->op_openowner->oo_confirmed = 1; return nfs_ok; } @@ -2573,14 +2579,14 @@ static __be32 nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_stateid **stpp) { struct nfs4_stateid *local; - struct nfs4_stateowner *sop = open->op_stateowner; + struct nfs4_openowner *oo = open->op_openowner; list_for_each_entry(local, &fp->fi_stateids, st_perfile) { /* ignore lock owners */ if (local->st_stateowner->so_is_open_owner == 0) continue; /* remember if we have seen this open owner */ - if (local->st_stateowner == sop) + if (local->st_stateowner == &oo->oo_owner) *stpp = local; /* check for conflicting share reservations */ if (!test_share(local, open)) @@ -2698,8 +2704,8 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c static void nfs4_set_claim_prev(struct nfsd4_open *open) { - open->op_stateowner->so_confirmed = 1; - open->op_stateowner->so_client->cl_firststate = 1; + open->op_openowner->oo_confirmed = 1; + open->op_openowner->oo_owner.so_client->cl_firststate = 1; } /* Should we give out recallable state?: */ @@ -2782,11 +2788,11 @@ static void nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_stateid *stp) { struct nfs4_delegation *dp; - struct nfs4_stateowner *sop = stp->st_stateowner; + struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner); int cb_up; int status, flag = 0; - cb_up = nfsd4_cb_channel_good(sop->so_client); + cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client); flag = NFS4_OPEN_DELEGATE_NONE; open->op_recall = 0; switch (open->op_claim_type) { @@ -2802,7 +2808,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta * had the chance to reclaim theirs.... */ if (locks_in_grace()) goto out; - if (!cb_up || !sop->so_confirmed) + if (!cb_up || !oo->oo_confirmed) goto out; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) flag = NFS4_OPEN_DELEGATE_WRITE; @@ -2813,7 +2819,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta goto out; } - dp = alloc_init_deleg(sop->so_client, stp, fh, flag); + dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh, flag); if (dp == NULL) goto out_no_deleg; status = nfs4_set_delegation(dp, flag); @@ -2901,7 +2907,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); if (nfsd4_has_session(&resp->cstate)) - open->op_stateowner->so_confirmed = 1; + open->op_openowner->oo_confirmed = 1; /* * Attempt to hand out a delegation. No error return, because the @@ -2922,7 +2928,7 @@ out: * To finish the open response, we just need to set the rflags. */ open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX; - if (!open->op_stateowner->so_confirmed && + if (!open->op_openowner->oo_confirmed && !nfsd4_has_session(&resp->cstate)) open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM; @@ -2981,7 +2987,7 @@ static time_t nfs4_laundromat(void) { struct nfs4_client *clp; - struct nfs4_stateowner *sop; + struct nfs4_openowner *oo; struct nfs4_delegation *dp; struct list_head *pos, *next, reaplist; time_t cutoff = get_seconds() - nfsd4_lease; @@ -3038,16 +3044,16 @@ nfs4_laundromat(void) } test_val = nfsd4_lease; list_for_each_safe(pos, next, &close_lru) { - sop = list_entry(pos, struct nfs4_stateowner, so_close_lru); - if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) { - u = sop->so_time - cutoff; + oo = container_of(pos, struct nfs4_openowner, oo_close_lru); + if (time_after((unsigned long)oo->oo_time, (unsigned long)cutoff)) { + u = oo->oo_time - cutoff; if (test_val > u) test_val = u; break; } dprintk("NFSD: purging unused open stateowner (so_id %d)\n", - sop->so_id); - release_openowner(sop); + oo->oo_owner.so_id); + release_openowner(oo); } if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT) clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT; @@ -3069,13 +3075,12 @@ laundromat_main(struct work_struct *not_used) queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); } -static struct nfs4_stateowner * -search_close_lru(u32 st_id) +static struct nfs4_openowner * search_close_lru(u32 st_id) { - struct nfs4_stateowner *local; + struct nfs4_openowner *local; - list_for_each_entry(local, &close_lru, so_close_lru) { - if (local->so_id == st_id) + list_for_each_entry(local, &close_lru, oo_close_lru) { + if (local->oo_owner.so_id == st_id) return local; } return NULL; @@ -3209,7 +3214,8 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) goto out; status = nfserr_bad_stateid; - if (!stp->st_stateowner->so_confirmed) + if (stp->st_stateowner->so_is_open_owner + && !openowner(stp->st_stateowner)->oo_confirmed) goto out; status = check_stateid_generation(stateid, &stp->st_stateid, has_session); @@ -3274,7 +3280,8 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, status = nfserr_bad_stateid; if (nfs4_check_fh(current_fh, stp)) goto out; - if (!stp->st_stateowner->so_confirmed) + if (stp->st_stateowner->so_is_open_owner + && !openowner(stp->st_stateowner)->oo_confirmed) goto out; status = check_stateid_generation(stateid, &stp->st_stateid, nfsd4_has_session(cstate)); @@ -3308,7 +3315,7 @@ nfsd4_free_delegation_stateid(stateid_t *stateid) static __be32 nfsd4_free_lock_stateid(struct nfs4_stateid *stp) { - if (check_for_locks(stp->st_file, stp->st_stateowner)) + if (check_for_locks(stp->st_file, lockowner(stp->st_stateowner))) return nfserr_locks_held; release_lock_stateid(stp); return nfs_ok; @@ -3417,7 +3424,8 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, if (status) return status; - if (!sop->so_confirmed && !(flags & CONFIRM)) { + if (sop->so_is_open_owner && !openowner(sop)->oo_confirmed + && !(flags & CONFIRM)) { dprintk("NFSD: preprocess_seqid_op: stateowner not" " confirmed yet!\n"); return nfserr_bad_stateid; @@ -3434,7 +3442,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_open_confirm *oc) { __be32 status; - struct nfs4_stateowner *sop; + struct nfs4_openowner *oo; struct nfs4_stateid *stp; dprintk("NFSD: nfsd4_open_confirm on file %.*s\n", @@ -3452,17 +3460,17 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, CONFIRM | OPEN_STATE, &stp); if (status) goto out; - sop = stp->st_stateowner; + oo = openowner(stp->st_stateowner); status = nfserr_bad_stateid; - if (sop->so_confirmed) + if (oo->oo_confirmed) goto out; - sop->so_confirmed = 1; + oo->oo_confirmed = 1; update_stateid(&stp->st_stateid); memcpy(&oc->oc_resp_stateid, &stp->st_stateid, sizeof(stateid_t)); dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stateid)); - nfsd4_create_clid_dir(sop->so_client); + nfsd4_create_clid_dir(oo->oo_owner.so_client); status = nfs_ok; out: if (!cstate->replay_owner) @@ -3513,7 +3521,6 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, &od->od_stateid, OPEN_STATE, &stp); if (status) goto out; - status = nfserr_inval; if (!test_bit(od->od_share_access, &stp->st_access_bmap)) { dprintk("NFSD:access not a subset current bitmap: 0x%lx, input access=%08x\n", @@ -3546,8 +3553,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_close *close) { __be32 status; + struct nfs4_openowner *oo; struct nfs4_stateid *stp; - struct nfs4_stateowner *so; dprintk("NFSD: nfsd4_close on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3563,19 +3570,19 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * Also, we should make sure this isn't just the result of * a replayed close: */ - so = search_close_lru(close->cl_stateid.si_stateownerid); + oo = search_close_lru(close->cl_stateid.si_stateownerid); /* It's not stale; let's assume it's expired: */ - if (so == NULL) + if (oo == NULL) goto out; - cstate->replay_owner = so; - status = nfsd4_check_seqid(cstate, so, close->cl_seqid); + cstate->replay_owner = &oo->oo_owner; + status = nfsd4_check_seqid(cstate, &oo->oo_owner, close->cl_seqid); if (status) goto out; status = nfserr_bad_seqid; } if (status) goto out; - so = stp->st_stateowner; + oo = openowner(stp->st_stateowner); status = nfs_ok; update_stateid(&stp->st_stateid); memcpy(&close->cl_stateid, &stp->st_stateid, sizeof(stateid_t)); @@ -3587,8 +3594,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * released by the laundromat service after the lease period * to enable us to handle CLOSE replay */ - if (list_empty(&so->so_stateids)) - move_to_close_lru(so); + if (list_empty(&oo->oo_owner.so_stateids)) + move_to_close_lru(oo); out: if (!cstate->replay_owner) nfs4_unlock_state(); @@ -3768,17 +3775,17 @@ static const struct lock_manager_operations nfsd_posix_mng_ops = { static inline void nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) { - struct nfs4_stateowner *sop; + struct nfs4_lockowner *lo; if (fl->fl_lmops == &nfsd_posix_mng_ops) { - sop = (struct nfs4_stateowner *) fl->fl_owner; - deny->ld_owner.data = kmemdup(sop->so_owner.data, - sop->so_owner.len, GFP_KERNEL); + lo = (struct nfs4_lockowner *) fl->fl_owner; + deny->ld_owner.data = kmemdup(lo->lo_owner.so_owner.data, + lo->lo_owner.so_owner.len, GFP_KERNEL); if (!deny->ld_owner.data) /* We just don't care that much */ goto nevermind; - deny->ld_owner.len = sop->so_owner.len; - deny->ld_clientid = sop->so_client->cl_clientid; + deny->ld_owner.len = lo->lo_owner.so_owner.len; + deny->ld_clientid = lo->lo_owner.so_client->cl_clientid; } else { nevermind: deny->ld_owner.len = 0; @@ -3795,8 +3802,8 @@ nevermind: deny->ld_type = NFS4_WRITE_LT; } -static struct nfs4_stateowner * -find_lockstateowner_str(struct inode *inode, clientid_t *clid, +static struct nfs4_lockowner * +find_lockowner_str(struct inode *inode, clientid_t *clid, struct xdr_netobj *owner) { unsigned int hashval = lock_ownerstr_hashval(inode, clid->cl_id, owner); @@ -3804,19 +3811,19 @@ find_lockstateowner_str(struct inode *inode, clientid_t *clid, list_for_each_entry(op, &lock_ownerstr_hashtbl[hashval], so_strhash) { if (same_owner_str(op, owner, clid)) - return op; + return lockowner(op); } return NULL; } -static void hash_lockowner(struct nfs4_stateowner *sop, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp) +static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp) { unsigned int idhashval; - idhashval = lockownerid_hashval(sop->so_id); - list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); - list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); - list_add(&sop->so_perstateid, &open_stp->st_lockowners); + idhashval = lockownerid_hashval(lo->lo_owner.so_id); + list_add(&lo->lo_owner.so_idhash, &lock_ownerid_hashtbl[idhashval]); + list_add(&lo->lo_owner.so_strhash, &lock_ownerstr_hashtbl[strhashval]); + list_add(&lo->lo_perstateid, &open_stp->st_lockowners); } /* @@ -3827,28 +3834,27 @@ static void hash_lockowner(struct nfs4_stateowner *sop, unsigned int strhashval, * strhashval = lock_ownerstr_hashval */ -static struct nfs4_stateowner * +static struct nfs4_lockowner * alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp, struct nfsd4_lock *lock) { - struct nfs4_stateowner *sop; + struct nfs4_lockowner *lo; - sop = alloc_stateowner(&lock->lk_new_owner, clp); - if (!sop) + lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp); + if (!lo) return NULL; - INIT_LIST_HEAD(&sop->so_stateids); - sop->so_is_open_owner = 0; + INIT_LIST_HEAD(&lo->lo_owner.so_stateids); + lo->lo_owner.so_is_open_owner = 0; /* It is the openowner seqid that will be incremented in encode in the * case of new lockowners; so increment the lock seqid manually: */ - sop->so_seqid = lock->lk_new_lock_seqid + 1; - sop->so_confirmed = 1; - hash_lockowner(sop, strhashval, clp, open_stp); - return sop; + lo->lo_owner.so_seqid = lock->lk_new_lock_seqid + 1; + hash_lockowner(lo, strhashval, clp, open_stp); + return lo; } static struct nfs4_stateid * -alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp) +alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct nfs4_stateid *open_stp) { struct nfs4_stateid *stp; - unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); + unsigned int hashval = stateid_hashval(lo->lo_owner.so_id, fp->fi_id); stp = nfs4_alloc_stateid(); if (stp == NULL) @@ -3859,13 +3865,13 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc INIT_LIST_HEAD(&stp->st_lockowners); /* not used */ list_add(&stp->st_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perfile, &fp->fi_stateids); - list_add(&stp->st_perstateowner, &sop->so_stateids); - stp->st_stateowner = sop; + list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); + stp->st_stateowner = &lo->lo_owner; stp->st_type = NFS4_LOCK_STID; get_nfs4_file(fp); stp->st_file = fp; stp->st_stateid.si_boot = boot_time; - stp->st_stateid.si_stateownerid = sop->so_id; + stp->st_stateid.si_stateownerid = lo->lo_owner.so_id; stp->st_stateid.si_fileid = fp->fi_id; /* note will be incremented before first return to client: */ stp->st_stateid.si_generation = 0; @@ -3902,8 +3908,8 @@ __be32 nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_lock *lock) { - struct nfs4_stateowner *open_sop = NULL; - struct nfs4_stateowner *lock_sop = NULL; + struct nfs4_openowner *open_sop = NULL; + struct nfs4_lockowner *lock_sop = NULL; struct nfs4_stateid *lock_stp; struct nfs4_file *fp; struct file *filp = NULL; @@ -3949,23 +3955,23 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, OPEN_STATE, &open_stp); if (status) goto out; + open_sop = openowner(open_stp->st_stateowner); status = nfserr_bad_stateid; - open_sop = open_stp->st_stateowner; if (!nfsd4_has_session(cstate) && - !same_clid(&open_sop->so_client->cl_clientid, + !same_clid(&open_sop->oo_owner.so_client->cl_clientid, &lock->v.new.clientid)) goto out; /* create lockowner and lock stateid */ fp = open_stp->st_file; - strhashval = lock_ownerstr_hashval(fp->fi_inode, - open_sop->so_client->cl_clientid.cl_id, + strhashval = lock_ownerstr_hashval(fp->fi_inode, + open_sop->oo_owner.so_client->cl_clientid.cl_id, &lock->v.new.owner); /* XXX: Do we need to check for duplicate stateowners on * the same file, or should they just be allowed (and * create new stateids)? */ status = nfserr_jukebox; lock_sop = alloc_init_lock_stateowner(strhashval, - open_sop->so_client, open_stp, lock); + open_sop->oo_owner.so_client, open_stp, lock); if (lock_sop == NULL) goto out; lock_stp = alloc_init_lock_stateid(lock_sop, fp, open_stp); @@ -3974,12 +3980,12 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } else { /* lock (lock owner + lock stateid) already exists */ status = nfs4_preprocess_seqid_op(cstate, - lock->lk_old_lock_seqid, - &lock->lk_old_lock_stateid, + lock->lk_old_lock_seqid, + &lock->lk_old_lock_stateid, LOCK_STATE, &lock_stp); if (status) goto out; - lock_sop = lock_stp->st_stateowner; + lock_sop = lockowner(lock_stp->st_stateowner); fp = lock_stp->st_file; } /* lock_sop and lock_stp have been created or found */ @@ -4092,7 +4098,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct inode *inode; struct file_lock file_lock; - struct nfs4_stateowner *so; + struct nfs4_lockowner *lo; int error; __be32 status; @@ -4128,10 +4134,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - so = find_lockstateowner_str(inode, - &lockt->lt_clientid, &lockt->lt_owner); - if (so) - file_lock.fl_owner = (fl_owner_t)so; + lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner); + if (lo) + file_lock.fl_owner = (fl_owner_t)lo; file_lock.fl_pid = current->tgid; file_lock.fl_flags = FL_POSIX; @@ -4186,7 +4191,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, BUG_ON(!filp); locks_init_lock(&file_lock); file_lock.fl_type = F_UNLCK; - file_lock.fl_owner = (fl_owner_t) stp->st_stateowner; + file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner); file_lock.fl_pid = current->tgid; file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; @@ -4225,7 +4230,7 @@ out_nfserr: * 0: no locks held by lockowner */ static int -check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner) +check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner) { struct file_lock **flpp; struct inode *inode = filp->fi_inode; @@ -4250,6 +4255,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, { clientid_t *clid = &rlockowner->rl_clientid; struct nfs4_stateowner *sop; + struct nfs4_lockowner *lo; struct nfs4_stateid *stp; struct xdr_netobj *owner = &rlockowner->rl_owner; struct list_head matches; @@ -4279,11 +4285,10 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, continue; list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) { - if (check_for_locks(stp->st_file, sop)) + lo = lockowner(sop); + if (check_for_locks(stp->st_file, lo)) goto out; - /* Note: so_perclient unused for lockowners, - * so it's OK to fool with here. */ - list_add(&sop->so_perclient, &matches); + list_add(&lo->lo_list, &matches); } } } @@ -4292,12 +4297,12 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, * have been checked. */ status = nfs_ok; while (!list_empty(&matches)) { - sop = list_entry(matches.next, struct nfs4_stateowner, - so_perclient); + lo = list_entry(matches.next, struct nfs4_lockowner, + lo_list); /* unhash_stateowner deletes so_perclient only * for openowners. */ - list_del(&sop->so_perclient); - release_lockowner(sop); + list_del(&lo->lo_list); + release_lockowner(lo); } out: nfs4_unlock_state(); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index c4dcba3aac1f..182570bed472 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -646,7 +646,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) memset(open->op_bmval, 0, sizeof(open->op_bmval)); open->op_iattr.ia_valid = 0; - open->op_stateowner = NULL; + open->op_openowner = NULL; /* seqid, share_access, share_deny, clientid, ownerlen */ READ_BUF(16 + sizeof(clientid_t)); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 0d88000d15d7..7994ed9be3cc 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -337,14 +337,11 @@ struct nfs4_replay { * reaped (when so_perfilestate is empty) to hold the last close replay. * reaped by laundramat thread after lease period. */ + struct nfs4_stateowner { struct list_head so_idhash; /* hash by so_id */ struct list_head so_strhash; /* hash by op_name */ - struct list_head so_perclient; struct list_head so_stateids; - struct list_head so_perstateid; /* for lockowners only */ - struct list_head so_close_lru; /* tail queue */ - time_t so_time; /* time of placement on so_close_lru */ int so_is_open_owner; /* 1=openowner,0=lockowner */ u32 so_id; struct nfs4_client * so_client; @@ -352,10 +349,33 @@ struct nfs4_stateowner { * sequence id expected from the client: */ u32 so_seqid; struct xdr_netobj so_owner; /* open owner name */ - int so_confirmed; /* successful OPEN_CONFIRM? */ struct nfs4_replay so_replay; }; +struct nfs4_openowner { + struct nfs4_stateowner oo_owner; /* must be first field */ + struct list_head oo_perclient; + struct list_head oo_close_lru; /* tail queue */ + time_t oo_time; /* time of placement on so_close_lru */ + int oo_confirmed; /* successful OPEN_CONFIRM? */ +}; + +struct nfs4_lockowner { + struct nfs4_stateowner lo_owner; /* must be first element */ + struct list_head lo_perstateid; /* for lockowners only */ + struct list_head lo_list; /* for temporary uses */ +}; + +static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so) +{ + return container_of(so, struct nfs4_openowner, oo_owner); +} + +static inline struct nfs4_lockowner * lockowner(struct nfs4_stateowner *so) +{ + return container_of(so, struct nfs4_lockowner, lo_owner); +} + /* * nfs4_file: a file opened by some number of (open) nfs4_stateowners. * o fi_perfile list is used to search for conflicting @@ -457,7 +477,8 @@ extern void nfs4_lock_state(void); extern void nfs4_unlock_state(void); extern int nfs4_in_grace(void); extern __be32 nfs4_check_open_reclaim(clientid_t *clid); -extern void nfs4_free_stateowner(struct nfs4_stateowner *sop); +extern void nfs4_free_openowner(struct nfs4_openowner *); +extern void nfs4_free_lockowner(struct nfs4_lockowner *); extern int set_callback_cred(void); extern void nfsd4_probe_callback(struct nfs4_client *clp); extern void nfsd4_probe_callback_sync(struct nfs4_client *clp); diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 27a3dfab96a9..f95a72482064 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -227,7 +227,7 @@ struct nfsd4_open { struct nfsd4_change_info op_cinfo; /* response */ u32 op_rflags; /* response */ int op_truncate; /* used during processing */ - struct nfs4_stateowner *op_stateowner; /* used during processing */ + struct nfs4_openowner *op_openowner; /* used during processing */ struct nfs4_acl *op_acl; }; #define op_iattr iattr From 4665e2bac5076d02264f4a4d79edafa05ec7b752 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 6 Sep 2011 14:50:49 -0400 Subject: [PATCH 047/103] nfsd4: split out some free_generic_stateid code We'll use this elsewhere. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 567130dccda0..c28432a80210 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -411,7 +411,7 @@ static void unhash_generic_stateid(struct nfs4_stateid *stp) list_del(&stp->st_perstateowner); } -static void free_generic_stateid(struct nfs4_stateid *stp) +static void close_generic_stateid(struct nfs4_stateid *stp) { int i; @@ -420,9 +420,16 @@ static void free_generic_stateid(struct nfs4_stateid *stp) if (test_bit(i, &stp->st_access_bmap)) nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(i)); + __clear_bit(i, &stp->st_access_bmap); } } put_nfs4_file(stp->st_file); + stp->st_file = NULL; +} + +static void free_generic_stateid(struct nfs4_stateid *stp) +{ + close_generic_stateid(stp); kmem_cache_free(stateid_slab, stp); } From 4581d1409977c5fe686a4ed487cdce2e50031826 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 6 Sep 2011 14:56:09 -0400 Subject: [PATCH 048/103] nfsd4: rearrange to avoid a forward reference Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 53 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c28432a80210..f0eccc236a0d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -60,7 +60,6 @@ static u64 current_sessionid = 1; #define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) /* forward declarations */ -static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); static struct nfs4_delegation * search_for_delegation(stateid_t *stid); static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner); @@ -1061,6 +1060,32 @@ static void gen_confirm(struct nfs4_client *clp) *p++ = i++; } +static int +same_stateid(stateid_t *id_one, stateid_t *id_two) +{ + if (id_one->si_stateownerid != id_two->si_stateownerid) + return 0; + return id_one->si_fileid == id_two->si_fileid; +} + +static struct nfs4_stateid *find_stateid(stateid_t *t, int flags) +{ + struct nfs4_stateid *s; + unsigned int hashval; + + hashval = stateid_hashval(t->si_stateownerid, t->si_fileid); + list_for_each_entry(s, &stateid_hashtbl[hashval], st_hash) { + if (!same_stateid(&s->st_stateid, t)) + continue; + if (flags & LOCK_STATE && s->st_type != NFS4_LOCK_STID) + return NULL; + if (flags & OPEN_STATE && s->st_type != NFS4_OPEN_STID) + return NULL; + return s; + } + return NULL; +} + static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, struct svc_rqst *rqstp, nfs4_verifier *verf) { @@ -3694,32 +3719,6 @@ lock_ownerstr_hashval(struct inode *inode, u32 cl_id, static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE]; static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; -static int -same_stateid(stateid_t *id_one, stateid_t *id_two) -{ - if (id_one->si_stateownerid != id_two->si_stateownerid) - return 0; - return id_one->si_fileid == id_two->si_fileid; -} - -static struct nfs4_stateid *find_stateid(stateid_t *t, int flags) -{ - struct nfs4_stateid *s; - unsigned int hashval; - - hashval = stateid_hashval(t->si_stateownerid, t->si_fileid); - list_for_each_entry(s, &stateid_hashtbl[hashval], st_hash) { - if (!same_stateid(&s->st_stateid, t)) - continue; - if (flags & LOCK_STATE && s->st_type != NFS4_LOCK_STID) - return NULL; - if (flags & OPEN_STATE && s->st_type != NFS4_OPEN_STID) - return NULL; - return s; - } - return NULL; -} - static struct nfs4_delegation * search_for_delegation(stateid_t *stid) { From 4d71ab8751c1d749f9fdf84ec094989adf579493 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 6 Sep 2011 16:48:57 -0400 Subject: [PATCH 049/103] nfsd4: split up find_stateid Minor cleanup. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f0eccc236a0d..aa088bc3b169 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1068,21 +1068,29 @@ same_stateid(stateid_t *id_one, stateid_t *id_two) return id_one->si_fileid == id_two->si_fileid; } -static struct nfs4_stateid *find_stateid(stateid_t *t, int flags) +static struct nfs4_stateid *find_stateid(stateid_t *t) { struct nfs4_stateid *s; unsigned int hashval; hashval = stateid_hashval(t->si_stateownerid, t->si_fileid); - list_for_each_entry(s, &stateid_hashtbl[hashval], st_hash) { - if (!same_stateid(&s->st_stateid, t)) - continue; - if (flags & LOCK_STATE && s->st_type != NFS4_LOCK_STID) - return NULL; - if (flags & OPEN_STATE && s->st_type != NFS4_OPEN_STID) - return NULL; + list_for_each_entry(s, &stateid_hashtbl[hashval], st_hash) + if (same_stateid(&s->st_stateid, t)) + return s; + return NULL; +} + +static struct nfs4_stateid *find_stateid_by_type(stateid_t *t, int flags) +{ + struct nfs4_stateid *s; + + s = find_stateid(t); + if (!s) + return NULL; + if (flags & LOCK_STATE && s->st_type == NFS4_LOCK_STID) + return s; + if (flags & OPEN_STATE && s->st_type == NFS4_OPEN_STID) return s; - } return NULL; } @@ -3241,7 +3249,7 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) goto out; status = nfserr_expired; - stp = find_stateid(stateid, 0); + stp = find_stateid(stateid); if (!stp) goto out; status = nfserr_bad_stateid; @@ -3306,7 +3314,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, BUG_ON(!*filpp); } } else { /* open or lock stateid */ - stp = find_stateid(stateid, flags); + stp = find_stateid(stateid); if (!stp) goto out; status = nfserr_bad_stateid; @@ -3381,7 +3389,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - stp = find_stateid(stateid, 0); + stp = find_stateid(stateid); if (!stp) { ret = nfserr_bad_stateid; goto out; @@ -3440,7 +3448,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, * the confirmed flag is incorrecly set, or the generation * number is incorrect. */ - *stpp = find_stateid(stateid, flags); + *stpp = find_stateid_by_type(stateid, flags); if (*stpp == NULL) return nfserr_expired; From c0a5d93efbbb79117bdf7f5f81fba9d679c35dfa Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 6 Sep 2011 15:19:46 -0400 Subject: [PATCH 050/103] nfsd4: split preprocess_seqid, cleanup Move most of this into helper functions. Also move the non-CONFIRM case into caller, providing a helper function for that purpose. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 87 +++++++++++++++++++++++---------------------- fs/nfsd/state.h | 1 - 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index aa088bc3b169..ad20bbf0a1f8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3253,7 +3253,6 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) if (!stp) goto out; status = nfserr_bad_stateid; - if (stp->st_stateowner->so_is_open_owner && !openowner(stp->st_stateowner)->oo_confirmed) goto out; @@ -3418,6 +3417,29 @@ setlkflg (int type) RD_STATE : WR_STATE; } +static __be32 nfs4_nospecial_stateid_checks(stateid_t *stateid) +{ + if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) + return nfserr_bad_stateid; + if (STALE_STATEID(stateid)) + return nfserr_stale_stateid; + return nfs_ok; +} + +static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_t *stateid, u32 seqid, struct nfs4_stateid *stp) +{ + struct svc_fh *current_fh = &cstate->current_fh; + struct nfs4_stateowner *sop = stp->st_stateowner; + __be32 status; + + if (nfs4_check_fh(current_fh, stp)) + return nfserr_bad_stateid; + status = nfsd4_check_seqid(cstate, sop, seqid); + if (status) + return status; + return check_stateid_generation(stateid, &stp->st_stateid, nfsd4_has_session(cstate)); +} + /* * Checks for sequence id mutating operations. */ @@ -3426,54 +3448,36 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateid **stpp) { - struct nfs4_stateowner *sop; - struct svc_fh *current_fh = &cstate->current_fh; __be32 status; dprintk("NFSD: %s: seqid=%d stateid = " STATEID_FMT "\n", __func__, seqid, STATEID_VAL(stateid)); *stpp = NULL; - - if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) { - dprintk("NFSD: preprocess_seqid_op: magic stateid!\n"); - return nfserr_bad_stateid; - } - - if (STALE_STATEID(stateid)) - return nfserr_stale_stateid; - - /* - * We return BAD_STATEID if filehandle doesn't match stateid, - * the confirmed flag is incorrecly set, or the generation - * number is incorrect. - */ + status = nfs4_nospecial_stateid_checks(stateid); + if (status) + return status; *stpp = find_stateid_by_type(stateid, flags); if (*stpp == NULL) return nfserr_expired; + cstate->replay_owner = (*stpp)->st_stateowner; + renew_client((*stpp)->st_stateowner->so_client); - sop = (*stpp)->st_stateowner; - cstate->replay_owner = sop; + return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp); +} - if (nfs4_check_fh(current_fh, *stpp)) { - dprintk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n"); - return nfserr_bad_stateid; - } +static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, struct nfs4_stateid **stpp) +{ + __be32 status; + struct nfs4_openowner *oo; - status = nfsd4_check_seqid(cstate, sop, seqid); + status = nfs4_preprocess_seqid_op(cstate, seqid, stateid, + OPEN_STATE, stpp); if (status) return status; - - if (sop->so_is_open_owner && !openowner(sop)->oo_confirmed - && !(flags & CONFIRM)) { - dprintk("NFSD: preprocess_seqid_op: stateowner not" - " confirmed yet!\n"); + oo = openowner((*stpp)->st_stateowner); + if (!oo->oo_confirmed) return nfserr_bad_stateid; - } - status = check_stateid_generation(stateid, &(*stpp)->st_stateid, nfsd4_has_session(cstate)); - if (status) - return status; - renew_client(sop->so_client); return nfs_ok; } @@ -3497,7 +3501,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, oc->oc_seqid, &oc->oc_req_stateid, - CONFIRM | OPEN_STATE, &stp); + OPEN_STATE, &stp); if (status) goto out; oo = openowner(stp->st_stateowner); @@ -3557,8 +3561,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, return nfserr_inval; nfs4_lock_state(); - status = nfs4_preprocess_seqid_op(cstate, od->od_seqid, - &od->od_stateid, OPEN_STATE, &stp); + status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid, + &od->od_stateid, &stp); if (status) goto out; status = nfserr_inval; @@ -3602,9 +3606,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); /* check close_lru for replay */ - status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid, - &close->cl_stateid, - OPEN_STATE, &stp); + status = nfs4_preprocess_confirmed_seqid_op(cstate, close->cl_seqid, + &close->cl_stateid, &stp); if (stp == NULL && status == nfserr_expired) { /* * Also, we should make sure this isn't just the result of @@ -3963,10 +3966,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; /* validate and update open stateid and open seqid */ - status = nfs4_preprocess_seqid_op(cstate, + status = nfs4_preprocess_confirmed_seqid_op(cstate, lock->lk_new_open_seqid, &lock->lk_new_open_stateid, - OPEN_STATE, &open_stp); + &open_stp); if (status) goto out; open_sop = openowner(open_stp->st_stateowner); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 7994ed9be3cc..9745cc781e74 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -463,7 +463,6 @@ struct nfs4_stateid { }; /* flags for preprocess_seqid_op() */ -#define CONFIRM 0x00000002 #define OPEN_STATE 0x00000004 #define LOCK_STATE 0x00000008 #define RD_STATE 0x00000010 From 2288d0e3958b94bcc3c00a78ea06909a8eb66378 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 6 Sep 2011 15:50:21 -0400 Subject: [PATCH 051/103] nfsd4: pass around typemask instead of flags We're only using those flags to choose lock or open stateid's at this point. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 18 ++++++++---------- fs/nfsd/state.h | 2 -- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ad20bbf0a1f8..48134635fc2f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1080,16 +1080,14 @@ static struct nfs4_stateid *find_stateid(stateid_t *t) return NULL; } -static struct nfs4_stateid *find_stateid_by_type(stateid_t *t, int flags) +static struct nfs4_stateid *find_stateid_by_type(stateid_t *t, char typemask) { struct nfs4_stateid *s; s = find_stateid(t); if (!s) return NULL; - if (flags & LOCK_STATE && s->st_type == NFS4_LOCK_STID) - return s; - if (flags & OPEN_STATE && s->st_type == NFS4_OPEN_STID) + if (typemask & s->st_type) return s; return NULL; } @@ -3445,7 +3443,7 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_ */ static __be32 nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, - stateid_t *stateid, int flags, + stateid_t *stateid, char typemask, struct nfs4_stateid **stpp) { __be32 status; @@ -3457,7 +3455,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, status = nfs4_nospecial_stateid_checks(stateid); if (status) return status; - *stpp = find_stateid_by_type(stateid, flags); + *stpp = find_stateid_by_type(stateid, typemask); if (*stpp == NULL) return nfserr_expired; cstate->replay_owner = (*stpp)->st_stateowner; @@ -3472,7 +3470,7 @@ static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cs struct nfs4_openowner *oo; status = nfs4_preprocess_seqid_op(cstate, seqid, stateid, - OPEN_STATE, stpp); + NFS4_OPEN_STID, stpp); if (status) return status; oo = openowner((*stpp)->st_stateowner); @@ -3501,7 +3499,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, oc->oc_seqid, &oc->oc_req_stateid, - OPEN_STATE, &stp); + NFS4_OPEN_STID, &stp); if (status) goto out; oo = openowner(stp->st_stateowner); @@ -3999,7 +3997,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, lock->lk_old_lock_seqid, &lock->lk_old_lock_stateid, - LOCK_STATE, &lock_stp); + NFS4_LOCK_STID, &lock_stp); if (status) goto out; lock_sop = lockowner(lock_stp->st_stateowner); @@ -4197,7 +4195,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); status = nfs4_preprocess_seqid_op(cstate, locku->lu_seqid, - &locku->lu_stateid, LOCK_STATE, &stp); + &locku->lu_stateid, NFS4_LOCK_STID, &stp); if (status) goto out; filp = find_any_file(stp->st_file); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 9745cc781e74..ef949eb3a86e 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -463,8 +463,6 @@ struct nfs4_stateid { }; /* flags for preprocess_seqid_op() */ -#define OPEN_STATE 0x00000004 -#define LOCK_STATE 0x00000008 #define RD_STATE 0x00000010 #define WR_STATE 0x00000020 From 881ea2b11e8a786a8d30c108261296953380bead Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 6 Sep 2011 17:20:34 -0400 Subject: [PATCH 052/103] nfsd4: rename init_stateid Note this is actually open-stateid specific. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 48134635fc2f..73b5e1e264fa 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2314,7 +2314,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str } static inline void -init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { +init_open_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { struct nfs4_openowner *oo = open->op_openowner; unsigned int hashval = stateid_hashval(oo->oo_owner.so_id, fp->fi_id); @@ -2934,7 +2934,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status = nfs4_new_open(rqstp, &stp, fp, current_fh, open); if (status) goto out; - init_stateid(stp, fp, open); + init_open_stateid(stp, fp, open); status = nfsd4_truncate(rqstp, current_fh, open); if (status) { release_open_stateid(stp); From 91a8c04031d051db41ec2a8c15e24622da037382 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 7 Sep 2011 12:54:06 -0400 Subject: [PATCH 053/103] nfsd4: remove redundant stateid initialization Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 73b5e1e264fa..768382d1de97 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2318,10 +2318,7 @@ init_open_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_o struct nfs4_openowner *oo = open->op_openowner; unsigned int hashval = stateid_hashval(oo->oo_owner.so_id, fp->fi_id); - INIT_LIST_HEAD(&stp->st_hash); - INIT_LIST_HEAD(&stp->st_perstateowner); INIT_LIST_HEAD(&stp->st_lockowners); - INIT_LIST_HEAD(&stp->st_perfile); list_add(&stp->st_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); @@ -3874,10 +3871,6 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct stp = nfs4_alloc_stateid(); if (stp == NULL) goto out; - INIT_LIST_HEAD(&stp->st_hash); - INIT_LIST_HEAD(&stp->st_perfile); - INIT_LIST_HEAD(&stp->st_perstateowner); - INIT_LIST_HEAD(&stp->st_lockowners); /* not used */ list_add(&stp->st_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); From dcef0413da9a17bfca917d8b49baf309ce76b737 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 7 Sep 2011 16:06:42 -0400 Subject: [PATCH 054/103] nfsd4: move some of nfs4_stateid into a separate structure We want delegations to share more with open/lock stateid's, so first we'll pull out some of the common stuff we want to share. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 174 ++++++++++++++++++++++---------------------- fs/nfsd/state.h | 29 +++++--- 2 files changed, 106 insertions(+), 97 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 768382d1de97..d0bb5a5613a9 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -159,7 +159,7 @@ static struct list_head open_ownerstr_hashtbl[OPEN_OWNER_HASH_SIZE]; #define FILE_HASH_BITS 8 #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) -/* hash table for (open)nfs4_stateid */ +/* hash table for (open)nfs4_ol_stateid */ #define STATEID_HASH_BITS 10 #define STATEID_HASH_SIZE (1 << STATEID_HASH_BITS) #define STATEID_HASH_MASK (STATEID_HASH_SIZE - 1) @@ -219,7 +219,7 @@ static void nfs4_file_put_access(struct nfs4_file *fp, int oflag) } static struct nfs4_delegation * -alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_fh *current_fh, u32 type) +alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh, u32 type) { struct nfs4_delegation *dp; struct nfs4_file *fp = stp->st_file; @@ -380,7 +380,7 @@ set_deny(unsigned int *deny, unsigned long bmap) { } static int -test_share(struct nfs4_stateid *stp, struct nfsd4_open *open) { +test_share(struct nfs4_ol_stateid *stp, struct nfsd4_open *open) { unsigned int access, deny; set_access(&access, stp->st_access_bmap); @@ -403,14 +403,14 @@ static int nfs4_access_to_omode(u32 access) BUG(); } -static void unhash_generic_stateid(struct nfs4_stateid *stp) +static void unhash_generic_stateid(struct nfs4_ol_stateid *stp) { - list_del(&stp->st_hash); + list_del(&stp->st_stid.sc_hash); list_del(&stp->st_perfile); list_del(&stp->st_perstateowner); } -static void close_generic_stateid(struct nfs4_stateid *stp) +static void close_generic_stateid(struct nfs4_ol_stateid *stp) { int i; @@ -426,13 +426,13 @@ static void close_generic_stateid(struct nfs4_stateid *stp) stp->st_file = NULL; } -static void free_generic_stateid(struct nfs4_stateid *stp) +static void free_generic_stateid(struct nfs4_ol_stateid *stp) { close_generic_stateid(stp); kmem_cache_free(stateid_slab, stp); } -static void release_lock_stateid(struct nfs4_stateid *stp) +static void release_lock_stateid(struct nfs4_ol_stateid *stp) { struct file *file; @@ -445,14 +445,14 @@ static void release_lock_stateid(struct nfs4_stateid *stp) static void unhash_lockowner(struct nfs4_lockowner *lo) { - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; list_del(&lo->lo_owner.so_idhash); list_del(&lo->lo_owner.so_strhash); list_del(&lo->lo_perstateid); while (!list_empty(&lo->lo_owner.so_stateids)) { stp = list_first_entry(&lo->lo_owner.so_stateids, - struct nfs4_stateid, st_perstateowner); + struct nfs4_ol_stateid, st_perstateowner); release_lock_stateid(stp); } } @@ -464,7 +464,7 @@ static void release_lockowner(struct nfs4_lockowner *lo) } static void -release_stateid_lockowners(struct nfs4_stateid *open_stp) +release_stateid_lockowners(struct nfs4_ol_stateid *open_stp) { struct nfs4_lockowner *lo; @@ -475,7 +475,7 @@ release_stateid_lockowners(struct nfs4_stateid *open_stp) } } -static void release_open_stateid(struct nfs4_stateid *stp) +static void release_open_stateid(struct nfs4_ol_stateid *stp) { unhash_generic_stateid(stp); release_stateid_lockowners(stp); @@ -484,14 +484,14 @@ static void release_open_stateid(struct nfs4_stateid *stp) static void unhash_openowner(struct nfs4_openowner *oo) { - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; list_del(&oo->oo_owner.so_idhash); list_del(&oo->oo_owner.so_strhash); list_del(&oo->oo_perclient); while (!list_empty(&oo->oo_owner.so_stateids)) { stp = list_first_entry(&oo->oo_owner.so_stateids, - struct nfs4_stateid, st_perstateowner); + struct nfs4_ol_stateid, st_perstateowner); release_open_stateid(stp); } } @@ -1068,26 +1068,26 @@ same_stateid(stateid_t *id_one, stateid_t *id_two) return id_one->si_fileid == id_two->si_fileid; } -static struct nfs4_stateid *find_stateid(stateid_t *t) +static struct nfs4_ol_stateid *find_stateid(stateid_t *t) { - struct nfs4_stateid *s; + struct nfs4_stid *s; unsigned int hashval; hashval = stateid_hashval(t->si_stateownerid, t->si_fileid); - list_for_each_entry(s, &stateid_hashtbl[hashval], st_hash) - if (same_stateid(&s->st_stateid, t)) - return s; + list_for_each_entry(s, &stateid_hashtbl[hashval], sc_hash) + if (same_stateid(&s->sc_stateid, t)) + return openlockstateid(s); return NULL; } -static struct nfs4_stateid *find_stateid_by_type(stateid_t *t, char typemask) +static struct nfs4_ol_stateid *find_stateid_by_type(stateid_t *t, char typemask) { - struct nfs4_stateid *s; + struct nfs4_ol_stateid *s; s = find_stateid(t); if (!s) return NULL; - if (typemask & s->st_type) + if (typemask & s->st_stid.sc_type) return s; return NULL; } @@ -2232,7 +2232,7 @@ nfsd4_init_slabs(void) if (file_slab == NULL) goto out_nomem; stateid_slab = kmem_cache_create("nfsd4_stateids", - sizeof(struct nfs4_stateid), 0, 0, NULL); + sizeof(struct nfs4_ol_stateid), 0, 0, NULL); if (stateid_slab == NULL) goto out_nomem; deleg_slab = kmem_cache_create("nfsd4_delegations", @@ -2314,23 +2314,23 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str } static inline void -init_open_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { +init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { struct nfs4_openowner *oo = open->op_openowner; unsigned int hashval = stateid_hashval(oo->oo_owner.so_id, fp->fi_id); INIT_LIST_HEAD(&stp->st_lockowners); - list_add(&stp->st_hash, &stateid_hashtbl[hashval]); + list_add(&stp->st_stid.sc_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); - stp->st_type = NFS4_OPEN_STID; + stp->st_stid.sc_type = NFS4_OPEN_STID; stp->st_stateowner = &oo->oo_owner; get_nfs4_file(fp); stp->st_file = fp; - stp->st_stateid.si_boot = boot_time; - stp->st_stateid.si_stateownerid = oo->oo_owner.so_id; - stp->st_stateid.si_fileid = fp->fi_id; + stp->st_stid.sc_stateid.si_boot = boot_time; + stp->st_stid.sc_stateid.si_stateownerid = oo->oo_owner.so_id; + stp->st_stid.sc_stateid.si_fileid = fp->fi_id; /* note will be incremented before first return to client: */ - stp->st_stateid.si_generation = 0; + stp->st_stid.sc_stateid.si_generation = 0; stp->st_access_bmap = 0; stp->st_deny_bmap = 0; __set_bit(open->op_share_access & ~NFS4_SHARE_WANT_MASK, @@ -2422,7 +2422,7 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) { struct inode *ino = current_fh->fh_dentry->d_inode; struct nfs4_file *fp; - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; __be32 ret; dprintk("NFSD: nfs4_share_conflict\n"); @@ -2611,9 +2611,9 @@ out: } static __be32 -nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_stateid **stpp) +nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_stateid **stpp) { - struct nfs4_stateid *local; + struct nfs4_ol_stateid *local; struct nfs4_openowner *oo = open->op_openowner; list_for_each_entry(local, &fp->fi_stateids, st_perfile) { @@ -2630,7 +2630,7 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_state return nfs_ok; } -static inline struct nfs4_stateid * +static inline struct nfs4_ol_stateid * nfs4_alloc_stateid(void) { return kmem_cache_alloc(stateid_slab, GFP_KERNEL); @@ -2672,11 +2672,11 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, } static __be32 -nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, +nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_ol_stateid **stpp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfsd4_open *open) { - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; __be32 status; stp = nfs4_alloc_stateid(); @@ -2708,7 +2708,7 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, } static __be32 -nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_stateid *stp, struct nfsd4_open *open) +nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open) { u32 op_share_access = open->op_share_access & ~NFS4_SHARE_WANT_MASK; bool new_access; @@ -2820,7 +2820,7 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag) * Attempt to hand out a delegation. */ static void -nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_stateid *stp) +nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_stateid *stp) { struct nfs4_delegation *dp; struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner); @@ -2888,7 +2888,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf struct nfsd4_compoundres *resp = rqstp->rq_resp; struct nfs4_file *fp = NULL; struct inode *ino = current_fh->fh_dentry->d_inode; - struct nfs4_stateid *stp = NULL; + struct nfs4_ol_stateid *stp = NULL; struct nfs4_delegation *dp = NULL; __be32 status; @@ -2938,8 +2938,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf goto out; } } - update_stateid(&stp->st_stateid); - memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); + update_stateid(&stp->st_stid.sc_stateid); + memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); if (nfsd4_has_session(&resp->cstate)) open->op_openowner->oo_confirmed = 1; @@ -2953,7 +2953,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status = nfs_ok; dprintk("%s: stateid=" STATEID_FMT "\n", __func__, - STATEID_VAL(&stp->st_stateid)); + STATEID_VAL(&stp->st_stid.sc_stateid)); out: if (fp) put_nfs4_file(fp); @@ -3122,7 +3122,7 @@ static struct nfs4_openowner * search_close_lru(u32 st_id) } static inline int -nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp) +nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp) { return fhp->fh_dentry->d_inode != stp->st_file->fi_inode; } @@ -3153,7 +3153,7 @@ access_permit_write(unsigned long access_bmap) } static -__be32 nfs4_check_openmode(struct nfs4_stateid *stp, int flags) +__be32 nfs4_check_openmode(struct nfs4_ol_stateid *stp, int flags) { __be32 status = nfserr_openmode; @@ -3237,7 +3237,7 @@ static int is_delegation_stateid(stateid_t *stateid) __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) { - struct nfs4_stateid *stp = NULL; + struct nfs4_ol_stateid *stp = NULL; __be32 status = nfserr_stale_stateid; if (STALE_STATEID(stateid)) @@ -3252,7 +3252,7 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) && !openowner(stp->st_stateowner)->oo_confirmed) goto out; - status = check_stateid_generation(stateid, &stp->st_stateid, has_session); + status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, has_session); if (status) goto out; @@ -3268,7 +3268,7 @@ __be32 nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, stateid_t *stateid, int flags, struct file **filpp) { - struct nfs4_stateid *stp = NULL; + struct nfs4_ol_stateid *stp = NULL; struct nfs4_delegation *dp = NULL; struct svc_fh *current_fh = &cstate->current_fh; struct inode *ino = current_fh->fh_dentry->d_inode; @@ -3317,7 +3317,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, if (stp->st_stateowner->so_is_open_owner && !openowner(stp->st_stateowner)->oo_confirmed) goto out; - status = check_stateid_generation(stateid, &stp->st_stateid, + status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); if (status) goto out; @@ -3347,7 +3347,7 @@ nfsd4_free_delegation_stateid(stateid_t *stateid) } static __be32 -nfsd4_free_lock_stateid(struct nfs4_stateid *stp) +nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp) { if (check_for_locks(stp->st_file, lockowner(stp->st_stateowner))) return nfserr_locks_held; @@ -3374,7 +3374,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_free_stateid *free_stateid) { stateid_t *stateid = &free_stateid->fr_stateid; - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; __be32 ret; nfs4_lock_state(); @@ -3388,11 +3388,11 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ret = nfserr_bad_stateid; goto out; } - ret = check_stateid_generation(stateid, &stp->st_stateid, 1); + ret = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, 1); if (ret) goto out; - if (stp->st_type == NFS4_OPEN_STID) { + if (stp->st_stid.sc_type == NFS4_OPEN_STID) { ret = nfserr_locks_held; goto out; } else { @@ -3421,7 +3421,7 @@ static __be32 nfs4_nospecial_stateid_checks(stateid_t *stateid) return nfs_ok; } -static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_t *stateid, u32 seqid, struct nfs4_stateid *stp) +static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_t *stateid, u32 seqid, struct nfs4_ol_stateid *stp) { struct svc_fh *current_fh = &cstate->current_fh; struct nfs4_stateowner *sop = stp->st_stateowner; @@ -3432,7 +3432,7 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_ status = nfsd4_check_seqid(cstate, sop, seqid); if (status) return status; - return check_stateid_generation(stateid, &stp->st_stateid, nfsd4_has_session(cstate)); + return check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); } /* @@ -3441,7 +3441,7 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_ static __be32 nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, char typemask, - struct nfs4_stateid **stpp) + struct nfs4_ol_stateid **stpp) { __be32 status; @@ -3461,7 +3461,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp); } -static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, struct nfs4_stateid **stpp) +static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, struct nfs4_ol_stateid **stpp) { __be32 status; struct nfs4_openowner *oo; @@ -3482,7 +3482,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; struct nfs4_openowner *oo; - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; dprintk("NFSD: nfsd4_open_confirm on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3504,10 +3504,10 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (oo->oo_confirmed) goto out; oo->oo_confirmed = 1; - update_stateid(&stp->st_stateid); - memcpy(&oc->oc_resp_stateid, &stp->st_stateid, sizeof(stateid_t)); + update_stateid(&stp->st_stid.sc_stateid); + memcpy(&oc->oc_resp_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", - __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stateid)); + __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stid.sc_stateid)); nfsd4_create_clid_dir(oo->oo_owner.so_client); status = nfs_ok; @@ -3517,7 +3517,7 @@ out: return status; } -static inline void nfs4_file_downgrade(struct nfs4_stateid *stp, unsigned int to_access) +static inline void nfs4_file_downgrade(struct nfs4_ol_stateid *stp, unsigned int to_access) { int i; @@ -3545,7 +3545,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, struct nfsd4_open_downgrade *od) { __be32 status; - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3575,8 +3575,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap); - update_stateid(&stp->st_stateid); - memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t)); + update_stateid(&stp->st_stid.sc_stateid); + memcpy(&od->od_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); status = nfs_ok; out: if (!cstate->replay_owner) @@ -3593,7 +3593,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; struct nfs4_openowner *oo; - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; dprintk("NFSD: nfsd4_close on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3622,8 +3622,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; oo = openowner(stp->st_stateowner); status = nfs_ok; - update_stateid(&stp->st_stateid); - memcpy(&close->cl_stateid, &stp->st_stateid, sizeof(stateid_t)); + update_stateid(&stp->st_stid.sc_stateid); + memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); /* release_stateid() calls nfsd_close() if needed */ release_open_stateid(stp); @@ -3828,7 +3828,7 @@ find_lockowner_str(struct inode *inode, clientid_t *clid, return NULL; } -static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp) +static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_ol_stateid *open_stp) { unsigned int idhashval; @@ -3847,7 +3847,7 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s */ static struct nfs4_lockowner * -alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp, struct nfsd4_lock *lock) { +alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_ol_stateid *open_stp, struct nfsd4_lock *lock) { struct nfs4_lockowner *lo; lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp); @@ -3862,27 +3862,27 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str return lo; } -static struct nfs4_stateid * -alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct nfs4_stateid *open_stp) +static struct nfs4_ol_stateid * +alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct nfs4_ol_stateid *open_stp) { - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; unsigned int hashval = stateid_hashval(lo->lo_owner.so_id, fp->fi_id); stp = nfs4_alloc_stateid(); if (stp == NULL) goto out; - list_add(&stp->st_hash, &stateid_hashtbl[hashval]); + list_add(&stp->st_stid.sc_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); stp->st_stateowner = &lo->lo_owner; - stp->st_type = NFS4_LOCK_STID; + stp->st_stid.sc_type = NFS4_LOCK_STID; get_nfs4_file(fp); stp->st_file = fp; - stp->st_stateid.si_boot = boot_time; - stp->st_stateid.si_stateownerid = lo->lo_owner.so_id; - stp->st_stateid.si_fileid = fp->fi_id; + stp->st_stid.sc_stateid.si_boot = boot_time; + stp->st_stid.sc_stateid.si_stateownerid = lo->lo_owner.so_id; + stp->st_stid.sc_stateid.si_fileid = fp->fi_id; /* note will be incremented before first return to client: */ - stp->st_stateid.si_generation = 0; + stp->st_stid.sc_stateid.si_generation = 0; stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_openstp = open_stp; @@ -3898,7 +3898,7 @@ check_lock_length(u64 offset, u64 length) LOFF_OVERFLOW(offset, length))); } -static void get_lock_access(struct nfs4_stateid *lock_stp, u32 access) +static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access) { struct nfs4_file *fp = lock_stp->st_file; int oflag = nfs4_access_to_omode(access); @@ -3918,7 +3918,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct nfs4_openowner *open_sop = NULL; struct nfs4_lockowner *lock_sop = NULL; - struct nfs4_stateid *lock_stp; + struct nfs4_ol_stateid *lock_stp; struct nfs4_file *fp; struct file *filp = NULL; struct file_lock file_lock; @@ -3949,7 +3949,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * Use open owner and open stateid to create lock owner and * lock stateid. */ - struct nfs4_stateid *open_stp = NULL; + struct nfs4_ol_stateid *open_stp = NULL; status = nfserr_stale_clientid; if (!nfsd4_has_session(cstate) && @@ -4052,8 +4052,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock); switch (-err) { case 0: /* success! */ - update_stateid(&lock_stp->st_stateid); - memcpy(&lock->lk_resp_stateid, &lock_stp->st_stateid, + update_stateid(&lock_stp->st_stid.sc_stateid); + memcpy(&lock->lk_resp_stateid, &lock_stp->st_stid.sc_stateid, sizeof(stateid_t)); status = 0; break; @@ -4172,7 +4172,7 @@ __be32 nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_locku *locku) { - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; struct file *filp = NULL; struct file_lock file_lock; __be32 status; @@ -4220,8 +4220,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* * OK, unlock succeeded; the only thing left to do is update the stateid. */ - update_stateid(&stp->st_stateid); - memcpy(&locku->lu_stateid, &stp->st_stateid, sizeof(stateid_t)); + update_stateid(&stp->st_stid.sc_stateid); + memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); out: nfs4_unlock_state(); @@ -4264,7 +4264,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, clientid_t *clid = &rlockowner->rl_clientid; struct nfs4_stateowner *sop; struct nfs4_lockowner *lo; - struct nfs4_stateid *stp; + struct nfs4_ol_stateid *stp; struct xdr_netobj *owner = &rlockowner->rl_owner; struct list_head matches; int i; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index ef949eb3a86e..d7fffabb8d56 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -328,10 +328,10 @@ struct nfs4_replay { * for lock_owner * so_perclient: nfs4_client->cl_perclient entry - used when nfs4_client * struct is reaped. -* so_perfilestate: heads the list of nfs4_stateid (either open or lock) -* and is used to ensure no dangling nfs4_stateid references when we +* so_perfilestate: heads the list of nfs4_ol_stateid (either open or lock) +* and is used to ensure no dangling nfs4_ol_stateid references when we * release a stateowner. -* so_perlockowner: (open) nfs4_stateid->st_perlockowner entry - used when +* so_perlockowner: (open) nfs4_ol_stateid->st_perlockowner entry - used when * close is called to reap associated byte-range locks * so_close_lru: (open) stateowner is placed on this list instead of being * reaped (when so_perfilestate is empty) to hold the last close replay. @@ -430,9 +430,9 @@ static inline struct file *find_any_file(struct nfs4_file *f) } /* -* nfs4_stateid can either be an open stateid or (eventually) a lock stateid +* nfs4_ol_stateid can either be an open stateid or (eventually) a lock stateid * -* (open)nfs4_stateid: one per (open)nfs4_stateowner, nfs4_file +* (open)nfs4_ol_stateid: one per (open)nfs4_stateowner, nfs4_file * * st_hash: stateid_hashtbl[] entry or lockstateid_hashtbl entry * st_perfile: file_hashtbl[] entry. @@ -446,22 +446,31 @@ static inline struct file *find_any_file(struct nfs4_file *f) * we should consider defining separate structs for the two cases. */ -struct nfs4_stateid { +struct nfs4_stid { #define NFS4_OPEN_STID 1 #define NFS4_LOCK_STID 2 - char st_type; - struct list_head st_hash; + char sc_type; + struct list_head sc_hash; + stateid_t sc_stateid; +}; + +struct nfs4_ol_stateid { + struct nfs4_stid st_stid; struct list_head st_perfile; struct list_head st_perstateowner; struct list_head st_lockowners; struct nfs4_stateowner * st_stateowner; struct nfs4_file * st_file; - stateid_t st_stateid; unsigned long st_access_bmap; unsigned long st_deny_bmap; - struct nfs4_stateid * st_openstp; + struct nfs4_ol_stateid * st_openstp; }; +static inline struct nfs4_ol_stateid *openlockstateid(struct nfs4_stid *s) +{ + return container_of(s, struct nfs4_ol_stateid, st_stid); +} + /* flags for preprocess_seqid_op() */ #define RD_STATE 0x00000010 #define WR_STATE 0x00000020 From d5477a8db8134c481ad7b4b745f6defa119253e1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 8 Sep 2011 12:07:44 -0400 Subject: [PATCH 055/103] nfsd4: add common dl_stid field to delegation Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 2 +- fs/nfsd/nfs4state.c | 20 ++++++++++---------- fs/nfsd/state.h | 20 +++++++++++--------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 02eb4edf0ece..93b5e405ad38 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -351,7 +351,7 @@ static void encode_cb_recall4args(struct xdr_stream *xdr, __be32 *p; encode_nfs_cb_opnum4(xdr, OP_CB_RECALL); - encode_stateid4(xdr, &dp->dl_stateid); + encode_stateid4(xdr, &dp->dl_stid.sc_stateid); p = xdr_reserve_space(xdr, 4); *p++ = xdr_zero; /* truncate */ diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d0bb5a5613a9..3e3d605d3e73 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -247,10 +247,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv get_nfs4_file(fp); dp->dl_file = fp; dp->dl_type = type; - dp->dl_stateid.si_boot = boot_time; - dp->dl_stateid.si_stateownerid = current_delegid++; - dp->dl_stateid.si_fileid = 0; - dp->dl_stateid.si_generation = 1; + dp->dl_stid.sc_stateid.si_boot = boot_time; + dp->dl_stid.sc_stateid.si_stateownerid = current_delegid++; + dp->dl_stid.sc_stateid.si_fileid = 0; + dp->dl_stid.sc_stateid.si_generation = 1; fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); @@ -2572,7 +2572,7 @@ find_delegation_file(struct nfs4_file *fp, stateid_t *stid) spin_lock(&recall_lock); list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) - if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid) { + if (dp->dl_stid.sc_stateid.si_stateownerid == stid->si_stateownerid) { spin_unlock(&recall_lock); return dp; } @@ -2861,10 +2861,10 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_ if (status) goto out_free; - memcpy(&open->op_delegate_stateid, &dp->dl_stateid, sizeof(dp->dl_stateid)); + memcpy(&open->op_delegate_stateid, &dp->dl_stid.sc_stateid, sizeof(dp->dl_stid.sc_stateid)); dprintk("NFSD: delegation stateid=" STATEID_FMT "\n", - STATEID_VAL(&dp->dl_stateid)); + STATEID_VAL(&dp->dl_stid.sc_stateid)); out: if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS && flag == NFS4_OPEN_DELEGATE_NONE @@ -3296,7 +3296,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, dp = find_delegation_stateid(ino, stateid); if (!dp) goto out; - status = check_stateid_generation(stateid, &dp->dl_stateid, nfsd4_has_session(cstate)); + status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate)); if (status) goto out; status = nfs4_check_delegmode(dp, flags); @@ -3667,7 +3667,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, dp = find_delegation_stateid(inode, stateid); if (!dp) goto out; - status = check_stateid_generation(stateid, &dp->dl_stateid, nfsd4_has_session(cstate)); + status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate)); if (status) goto out; renew_client(dp->dl_client); @@ -3737,7 +3737,7 @@ search_for_delegation(stateid_t *stid) list_for_each_entry(fp, &file_hashtbl[i], fi_hash) { list_for_each(pos, &fp->fi_delegations) { dp = list_entry(pos, struct nfs4_delegation, dl_perfile); - if (same_stateid(&dp->dl_stateid, stid)) + if (same_stateid(&dp->dl_stid.sc_stateid, stid)) return dp; } } diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index d7fffabb8d56..e3ff7c9f9264 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -76,6 +76,15 @@ struct nfsd4_callback { bool cb_done; }; +struct nfs4_stid { +#define NFS4_OPEN_STID 1 +#define NFS4_LOCK_STID 2 +#define NFS4_DELEG_STID 4 + char sc_type; + struct list_head sc_hash; + stateid_t sc_stateid; +}; + struct nfs4_delegation { struct list_head dl_perfile; struct list_head dl_perclnt; @@ -86,7 +95,7 @@ struct nfs4_delegation { u32 dl_type; time_t dl_time; /* For recall: */ - stateid_t dl_stateid; + struct nfs4_stid dl_stid; struct knfsd_fh dl_fh; int dl_retries; struct nfsd4_callback dl_recall; @@ -446,14 +455,7 @@ static inline struct file *find_any_file(struct nfs4_file *f) * we should consider defining separate structs for the two cases. */ -struct nfs4_stid { -#define NFS4_OPEN_STID 1 -#define NFS4_LOCK_STID 2 - char sc_type; - struct list_head sc_hash; - stateid_t sc_stateid; -}; - +/* "ol" stands for "Open or Lock". Better suggestions welcome. */ struct nfs4_ol_stateid { struct nfs4_stid st_stid; struct list_head st_perfile; From 36d44c6038f6d3f8786187a5558c2f3b39a7af95 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 8 Sep 2011 12:16:03 -0400 Subject: [PATCH 056/103] nfsd4: share common stid-hashing helper function Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3e3d605d3e73..581a5d01b005 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -218,6 +218,15 @@ static void nfs4_file_put_access(struct nfs4_file *fp, int oflag) __nfs4_file_put_access(fp, oflag); } +static inline void hash_stid(struct nfs4_stid *stid) +{ + stateid_t *s = &stid->sc_stateid; + unsigned int hashval; + + hashval = stateid_hashval(s->si_stateownerid, s->si_fileid); + list_add(&stid->sc_hash, &stateid_hashtbl[hashval]); +} + static struct nfs4_delegation * alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh, u32 type) { @@ -2316,10 +2325,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str static inline void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { struct nfs4_openowner *oo = open->op_openowner; - unsigned int hashval = stateid_hashval(oo->oo_owner.so_id, fp->fi_id); INIT_LIST_HEAD(&stp->st_lockowners); - list_add(&stp->st_stid.sc_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); stp->st_stid.sc_type = NFS4_OPEN_STID; @@ -2331,6 +2338,7 @@ init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd stp->st_stid.sc_stateid.si_fileid = fp->fi_id; /* note will be incremented before first return to client: */ stp->st_stid.sc_stateid.si_generation = 0; + hash_stid(&stp->st_stid); stp->st_access_bmap = 0; stp->st_deny_bmap = 0; __set_bit(open->op_share_access & ~NFS4_SHARE_WANT_MASK, @@ -3866,12 +3874,10 @@ static struct nfs4_ol_stateid * alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct nfs4_ol_stateid *open_stp) { struct nfs4_ol_stateid *stp; - unsigned int hashval = stateid_hashval(lo->lo_owner.so_id, fp->fi_id); stp = nfs4_alloc_stateid(); if (stp == NULL) goto out; - list_add(&stp->st_stid.sc_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); stp->st_stateowner = &lo->lo_owner; @@ -3883,6 +3889,7 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct stp->st_stid.sc_stateid.si_fileid = fp->fi_id; /* note will be incremented before first return to client: */ stp->st_stid.sc_stateid.si_generation = 0; + hash_stid(&stp->st_stid); stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_openstp = open_stp; From f459e4535904e16ca9f0cc202c78345c332bbbad Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 9 Sep 2011 09:06:12 -0400 Subject: [PATCH 057/103] nfsd4: hash deleg stateid's like any other It's simpler to look up delegation stateid's in the same hash table as any other stateid. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 114 ++++++++++++++++++-------------------------- fs/nfsd/state.h | 5 ++ 2 files changed, 52 insertions(+), 67 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 581a5d01b005..24685a02690f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -60,8 +60,6 @@ static u64 current_sessionid = 1; #define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) /* forward declarations */ -static struct nfs4_delegation * search_for_delegation(stateid_t *stid); -static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner); /* Locking: */ @@ -256,10 +254,12 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv get_nfs4_file(fp); dp->dl_file = fp; dp->dl_type = type; + dp->dl_stid.sc_type = NFS4_DELEG_STID; dp->dl_stid.sc_stateid.si_boot = boot_time; dp->dl_stid.sc_stateid.si_stateownerid = current_delegid++; dp->dl_stid.sc_stateid.si_fileid = 0; dp->dl_stid.sc_stateid.si_generation = 1; + hash_stid(&dp->dl_stid); fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); @@ -292,6 +292,7 @@ static void nfs4_put_deleg_lease(struct nfs4_file *fp) static void unhash_delegation(struct nfs4_delegation *dp) { + list_del_init(&dp->dl_stid.sc_hash); list_del_init(&dp->dl_perclnt); spin_lock(&recall_lock); list_del_init(&dp->dl_perfile); @@ -1077,7 +1078,7 @@ same_stateid(stateid_t *id_one, stateid_t *id_two) return id_one->si_fileid == id_two->si_fileid; } -static struct nfs4_ol_stateid *find_stateid(stateid_t *t) +static struct nfs4_stid *find_stateid(stateid_t *t) { struct nfs4_stid *s; unsigned int hashval; @@ -1085,22 +1086,42 @@ static struct nfs4_ol_stateid *find_stateid(stateid_t *t) hashval = stateid_hashval(t->si_stateownerid, t->si_fileid); list_for_each_entry(s, &stateid_hashtbl[hashval], sc_hash) if (same_stateid(&s->sc_stateid, t)) - return openlockstateid(s); + return s; return NULL; } -static struct nfs4_ol_stateid *find_stateid_by_type(stateid_t *t, char typemask) +static struct nfs4_ol_stateid *find_ol_stateid(stateid_t *t) { - struct nfs4_ol_stateid *s; + struct nfs4_stid *s; s = find_stateid(t); if (!s) return NULL; - if (typemask & s->st_stid.sc_type) + return openlockstateid(s); +} + +static struct nfs4_stid *find_stateid_by_type(stateid_t *t, char typemask) +{ + struct nfs4_stid *s; + + s = find_stateid(t); + if (!s) + return NULL; + if (typemask & s->sc_type) return s; return NULL; } +static struct nfs4_ol_stateid *find_ol_stateid_by_type(stateid_t *t, char typemask) +{ + struct nfs4_stid *s; + + s = find_stateid_by_type(t, typemask); + if (!s) + return NULL; + return openlockstateid(s); +} + static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, struct svc_rqst *rqstp, nfs4_verifier *verf) { @@ -2573,21 +2594,6 @@ nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) return nfs_ok; } -static struct nfs4_delegation * -find_delegation_file(struct nfs4_file *fp, stateid_t *stid) -{ - struct nfs4_delegation *dp; - - spin_lock(&recall_lock); - list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) - if (dp->dl_stid.sc_stateid.si_stateownerid == stid->si_stateownerid) { - spin_unlock(&recall_lock); - return dp; - } - spin_unlock(&recall_lock); - return NULL; -} - static int share_access_to_flags(u32 share_access) { share_access &= ~NFS4_SHARE_WANT_MASK; @@ -2595,6 +2601,16 @@ static int share_access_to_flags(u32 share_access) return share_access == NFS4_SHARE_ACCESS_READ ? RD_STATE : WR_STATE; } +static struct nfs4_delegation *find_deleg_stateid(stateid_t *s) +{ + struct nfs4_stid *ret; + + ret = find_stateid_by_type(s, NFS4_DELEG_STID); + if (!ret) + return NULL; + return delegstateid(ret); +} + static __be32 nfs4_check_deleg(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_delegation **dp) @@ -2602,7 +2618,7 @@ nfs4_check_deleg(struct nfs4_file *fp, struct nfsd4_open *open, int flags; __be32 status = nfserr_bad_stateid; - *dp = find_delegation_file(fp, &open->op_delegate_stateid); + *dp = find_deleg_stateid(&open->op_delegate_stateid); if (*dp == NULL) goto out; flags = share_access_to_flags(open->op_share_access); @@ -3252,7 +3268,7 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) goto out; status = nfserr_expired; - stp = find_stateid(stateid); + stp = find_ol_stateid(stateid); if (!stp) goto out; status = nfserr_bad_stateid; @@ -3301,7 +3317,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, */ status = nfserr_expired; if (is_delegation_stateid(stateid)) { - dp = find_delegation_stateid(ino, stateid); + dp = find_deleg_stateid(stateid); if (!dp) goto out; status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate)); @@ -3316,7 +3332,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, BUG_ON(!*filpp); } } else { /* open or lock stateid */ - stp = find_stateid(stateid); + stp = find_ol_stateid(stateid); if (!stp) goto out; status = nfserr_bad_stateid; @@ -3348,9 +3364,10 @@ out: static __be32 nfsd4_free_delegation_stateid(stateid_t *stateid) { - struct nfs4_delegation *dp = search_for_delegation(stateid); + struct nfs4_delegation *dp = find_deleg_stateid(stateid); if (dp) return nfserr_locks_held; + return nfserr_bad_stateid; } @@ -3391,7 +3408,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - stp = find_stateid(stateid); + stp = find_ol_stateid(stateid); if (!stp) { ret = nfserr_bad_stateid; goto out; @@ -3460,7 +3477,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, status = nfs4_nospecial_stateid_checks(stateid); if (status) return status; - *stpp = find_stateid_by_type(stateid, typemask); + *stpp = find_ol_stateid_by_type(stateid, typemask); if (*stpp == NULL) return nfserr_expired; cstate->replay_owner = (*stpp)->st_stateowner; @@ -3672,7 +3689,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!is_delegation_stateid(stateid)) goto out; status = nfserr_expired; - dp = find_delegation_stateid(inode, stateid); + dp = find_deleg_stateid(stateid); if (!dp) goto out; status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate)); @@ -3733,43 +3750,6 @@ lock_ownerstr_hashval(struct inode *inode, u32 cl_id, static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE]; static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; -static struct nfs4_delegation * -search_for_delegation(stateid_t *stid) -{ - struct nfs4_file *fp; - struct nfs4_delegation *dp; - struct list_head *pos; - int i; - - for (i = 0; i < FILE_HASH_SIZE; i++) { - list_for_each_entry(fp, &file_hashtbl[i], fi_hash) { - list_for_each(pos, &fp->fi_delegations) { - dp = list_entry(pos, struct nfs4_delegation, dl_perfile); - if (same_stateid(&dp->dl_stid.sc_stateid, stid)) - return dp; - } - } - } - return NULL; -} - -static struct nfs4_delegation * -find_delegation_stateid(struct inode *ino, stateid_t *stid) -{ - struct nfs4_file *fp; - struct nfs4_delegation *dl; - - dprintk("NFSD: %s: stateid=" STATEID_FMT "\n", __func__, - STATEID_VAL(stid)); - - fp = find_file(ino); - if (!fp) - return NULL; - dl = find_delegation_file(fp, stid); - put_nfs4_file(fp); - return dl; -} - /* * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that * we can't properly handle lock requests that go beyond the (2^63 - 1)-th diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index e3ff7c9f9264..12c142436705 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -113,6 +113,11 @@ struct nfs4_cb_conn { struct svc_xprt *cb_xprt; /* minorversion 1 only */ }; +static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s) +{ + return container_of(s, struct nfs4_delegation, dl_stid); +} + /* Maximum number of slots per session. 160 is useful for long haul TCP */ #define NFSD_MAX_SLOTS_PER_SESSION 160 /* Maximum number of operations per session compound */ From 97b7e3b6d4ae838694b43f66b4f0b32b5ec7635b Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 9 Sep 2011 11:26:58 -0400 Subject: [PATCH 058/103] nfsd4: fix test_stateid for delegation stateid's Test_stateid should handle delegation stateid's as well. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 24685a02690f..30387f3d9881 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3261,28 +3261,26 @@ static int is_delegation_stateid(stateid_t *stateid) __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) { - struct nfs4_ol_stateid *stp = NULL; - __be32 status = nfserr_stale_stateid; + struct nfs4_stid *s; + struct nfs4_ol_stateid *ols; + __be32 status; if (STALE_STATEID(stateid)) - goto out; + return nfserr_stale_stateid; - status = nfserr_expired; - stp = find_ol_stateid(stateid); - if (!stp) - goto out; - status = nfserr_bad_stateid; - if (stp->st_stateowner->so_is_open_owner - && !openowner(stp->st_stateowner)->oo_confirmed) - goto out; - - status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, has_session); + s = find_stateid(stateid); + if (!s) + return nfserr_stale_stateid; + status = check_stateid_generation(stateid, &s->sc_stateid, has_session); if (status) - goto out; - - status = nfs_ok; -out: - return status; + return status; + if (!(s->sc_type & (NFS4_OPEN_STID | NFS4_LOCK_STID))) + return nfs_ok; + ols = openlockstateid(s); + if (ols->st_stateowner->so_is_open_owner + && !openowner(ols->st_stateowner)->oo_confirmed) + return nfserr_bad_stateid; + return nfs_ok; } /* From 69064a2764fe195f1478be3ea83d15abe5d71025 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 9 Sep 2011 11:54:57 -0400 Subject: [PATCH 059/103] nfsd4: use deleg changes to cleanup preprocess_stateid_op Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 30387f3d9881..ea338d0c62a1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3290,6 +3290,7 @@ __be32 nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, stateid_t *stateid, int flags, struct file **filpp) { + struct nfs4_stid *s; struct nfs4_ol_stateid *stp = NULL; struct nfs4_delegation *dp = NULL; struct svc_fh *current_fh = &cstate->current_fh; @@ -3314,13 +3315,14 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, * but that we can't find, is expired: */ status = nfserr_expired; - if (is_delegation_stateid(stateid)) { - dp = find_deleg_stateid(stateid); - if (!dp) - goto out; - status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate)); - if (status) - goto out; + s = find_stateid(stateid); + if (!s) + goto out; + status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); + if (status) + goto out; + if (s->sc_type == NFS4_DELEG_STID) { + dp = delegstateid(s); status = nfs4_check_delegmode(dp, flags); if (status) goto out; @@ -3330,19 +3332,13 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, BUG_ON(!*filpp); } } else { /* open or lock stateid */ - stp = find_ol_stateid(stateid); - if (!stp) - goto out; + stp = openlockstateid(s); status = nfserr_bad_stateid; if (nfs4_check_fh(current_fh, stp)) goto out; if (stp->st_stateowner->so_is_open_owner && !openowner(stp->st_stateowner)->oo_confirmed) goto out; - status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, - nfsd4_has_session(cstate)); - if (status) - goto out; status = nfs4_check_openmode(stp, flags); if (status) goto out; From ee626a77d3725a129391b1c85edd46f3b470cca9 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 11 Sep 2011 13:48:41 -0400 Subject: [PATCH 060/103] nfsd4: better stateid hashing First, we shouldn't care here about the structure of the opaque part of the stateid. Second, this hash is really dumb. (I'm not sure the replacement is much better, though--to look at it another patch.) Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ea338d0c62a1..0cd346477f29 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -168,9 +168,9 @@ static unsigned int file_hashval(struct inode *ino) return hash_ptr(ino, FILE_HASH_BITS); } -static unsigned int stateid_hashval(u32 owner_id, u32 file_id) +static unsigned int stateid_hashval(stateid_t *s) { - return (owner_id + file_id) & STATEID_HASH_MASK; + return opaque_hashval(&s->si_opaque, sizeof(stateid_opaque_t)) & STATEID_HASH_MASK; } static struct list_head file_hashtbl[FILE_HASH_SIZE]; @@ -221,7 +221,7 @@ static inline void hash_stid(struct nfs4_stid *stid) stateid_t *s = &stid->sc_stateid; unsigned int hashval; - hashval = stateid_hashval(s->si_stateownerid, s->si_fileid); + hashval = stateid_hashval(s); list_add(&stid->sc_hash, &stateid_hashtbl[hashval]); } @@ -1083,7 +1083,7 @@ static struct nfs4_stid *find_stateid(stateid_t *t) struct nfs4_stid *s; unsigned int hashval; - hashval = stateid_hashval(t->si_stateownerid, t->si_fileid); + hashval = stateid_hashval(t); list_for_each_entry(s, &stateid_hashtbl[hashval], sc_hash) if (same_stateid(&s->sc_stateid, t)) return s; From ed748aacb8e3318fa2cf24e1c197d35b5fd29605 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 12 Sep 2011 19:37:06 -0400 Subject: [PATCH 061/103] NFSD: Cleanup for nfsd4_path() The current code is sort of hackish in that it assumes a referral is always matched to an export. When we add support for junctions that may not be the case. We can replace nfsd4_path() with a function that encodes the components directly from the dentries. Since nfsd4_path is currently the only user of the 'ex_pathname' field in struct svc_export, this has the added benefit of allowing us to get rid of that. Signed-off-by: Trond Myklebust Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 4 +- fs/nfsd/nfs4xdr.c | 108 ++++++++++++++++++++++++++---------- include/linux/nfsd/export.h | 1 + 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index d491421cd708..99229b0c153e 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1009,7 +1009,7 @@ rqst_exp_parent(struct svc_rqst *rqstp, struct path *path) return exp; } -static struct svc_export *find_fsidzero_export(struct svc_rqst *rqstp) +struct svc_export *rqst_find_fsidzero_export(struct svc_rqst *rqstp) { u32 fsidv[2]; @@ -1029,7 +1029,7 @@ exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp) struct svc_export *exp; __be32 rv; - exp = find_fsidzero_export(rqstp); + exp = rqst_find_fsidzero_export(rqstp); if (IS_ERR(exp)) return nfserrno(PTR_ERR(exp)); rv = fh_compose(fhp, exp, exp->ex_path.dentry, NULL); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 182570bed472..5252d6681960 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1696,36 +1696,89 @@ static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location, } /* - * Return the path to an export point in the pseudo filesystem namespace - * Returned string is safe to use as long as the caller holds a reference - * to @exp. + * Encode a path in RFC3530 'pathname4' format */ -static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat) +static __be32 nfsd4_encode_path(const struct path *root, + const struct path *path, __be32 **pp, int *buflen) { - struct svc_fh tmp_fh; - char *path = NULL, *rootpath; - size_t rootlen; + struct path cur = { + .mnt = path->mnt, + .dentry = path->dentry, + }; + __be32 *p = *pp; + struct dentry **components = NULL; + unsigned int ncomponents = 0; + __be32 err = nfserr_jukebox; - fh_init(&tmp_fh, NFS4_FHSIZE); - *stat = exp_pseudoroot(rqstp, &tmp_fh); - if (*stat) - return NULL; - rootpath = tmp_fh.fh_export->ex_pathname; + dprintk("nfsd4_encode_components("); - path = exp->ex_pathname; - - rootlen = strlen(rootpath); - if (strncmp(path, rootpath, rootlen)) { - dprintk("nfsd: fs_locations failed;" - "%s is not contained in %s\n", path, rootpath); - *stat = nfserr_notsupp; - path = NULL; - goto out; + path_get(&cur); + /* First walk the path up to the nfsd root, and store the + * dentries/path components in an array. + */ + for (;;) { + if (cur.dentry == root->dentry && cur.mnt == root->mnt) + break; + if (cur.dentry == cur.mnt->mnt_root) { + if (follow_up(&cur)) + continue; + goto out_free; + } + if ((ncomponents & 15) == 0) { + struct dentry **new; + new = krealloc(components, + sizeof(*new) * (ncomponents + 16), + GFP_KERNEL); + if (!new) + goto out_free; + components = new; + } + components[ncomponents++] = cur.dentry; + cur.dentry = dget_parent(cur.dentry); } - path += rootlen; -out: - fh_put(&tmp_fh); - return path; + + *buflen -= 4; + if (*buflen < 0) + goto out_free; + WRITE32(ncomponents); + + while (ncomponents) { + struct dentry *dentry = components[ncomponents - 1]; + unsigned int len = dentry->d_name.len; + + *buflen -= 4 + (XDR_QUADLEN(len) << 2); + if (*buflen < 0) + goto out_free; + WRITE32(len); + WRITEMEM(dentry->d_name.name, len); + dprintk("/%s", dentry->d_name.name); + dput(dentry); + ncomponents--; + } + + *pp = p; + err = 0; +out_free: + dprintk(")\n"); + while (ncomponents) + dput(components[--ncomponents]); + kfree(components); + path_put(&cur); + return err; +} + +static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp, + const struct path *path, __be32 **pp, int *buflen) +{ + struct svc_export *exp_ps; + __be32 res; + + exp_ps = rqst_find_fsidzero_export(rqstp); + if (IS_ERR(exp_ps)) + return nfserrno(PTR_ERR(exp_ps)); + res = nfsd4_encode_path(&exp_ps->ex_path, path, pp, buflen); + exp_put(exp_ps); + return res; } /* @@ -1739,11 +1792,8 @@ static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp, int i; __be32 *p = *pp; struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs; - char *root = nfsd4_path(rqstp, exp, &status); - if (status) - return status; - status = nfsd4_encode_components('/', root, &p, buflen); + status = nfsd4_encode_fsloc_fsroot(rqstp, &exp->ex_path, &p, buflen); if (status) return status; if ((*buflen -= 4) < 0) diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 8a31a20efe7e..7ba3fd43f312 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -137,6 +137,7 @@ struct svc_export * rqst_exp_get_by_name(struct svc_rqst *, struct path *); struct svc_export * rqst_exp_parent(struct svc_rqst *, struct path *); +struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *); int exp_rootfh(struct auth_domain *, char *path, struct knfsd_fh *, int maxsize); __be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *); From 2f1ddda1749a223d1a05e16dc6ea28632b9ec570 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 12 Sep 2011 19:37:16 -0400 Subject: [PATCH 062/103] NFSD: Remove the ex_pathname field from struct svc_export There are no more users... Signed-off-by: Trond Myklebust Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 11 ----------- include/linux/nfsd/export.h | 1 - 2 files changed, 12 deletions(-) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 99229b0c153e..62f3b9074e84 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -317,7 +317,6 @@ static void svc_export_put(struct kref *ref) struct svc_export *exp = container_of(ref, struct svc_export, h.ref); path_put(&exp->ex_path); auth_domain_put(exp->ex_client); - kfree(exp->ex_pathname); nfsd4_fslocs_free(&exp->ex_fslocs); kfree(exp); } @@ -527,11 +526,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) exp.ex_client = dom; - err = -ENOMEM; - exp.ex_pathname = kstrdup(buf, GFP_KERNEL); - if (!exp.ex_pathname) - goto out2; - /* expiry */ err = -EINVAL; exp.h.expiry_time = get_expiry(&mesg); @@ -612,8 +606,6 @@ out4: nfsd4_fslocs_free(&exp.ex_fslocs); kfree(exp.ex_uuid); out3: - kfree(exp.ex_pathname); -out2: path_put(&exp.ex_path); out1: auth_domain_put(dom); @@ -677,7 +669,6 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem) new->ex_client = item->ex_client; new->ex_path.dentry = dget(item->ex_path.dentry); new->ex_path.mnt = mntget(item->ex_path.mnt); - new->ex_pathname = NULL; new->ex_fslocs.locations = NULL; new->ex_fslocs.locations_count = 0; new->ex_fslocs.migrated = 0; @@ -695,8 +686,6 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem) new->ex_fsid = item->ex_fsid; new->ex_uuid = item->ex_uuid; item->ex_uuid = NULL; - new->ex_pathname = item->ex_pathname; - item->ex_pathname = NULL; new->ex_fslocs.locations = item->ex_fslocs.locations; item->ex_fslocs.locations = NULL; new->ex_fslocs.locations_count = item->ex_fslocs.locations_count; diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 7ba3fd43f312..f85308e688fd 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -96,7 +96,6 @@ struct svc_export { struct auth_domain * ex_client; int ex_flags; struct path ex_path; - char *ex_pathname; uid_t ex_anon_uid; gid_t ex_anon_gid; int ex_fsid; From 11fcee0293a6d9f0973e04f8b3fb6cd15a55bcce Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 12 Sep 2011 19:37:26 -0400 Subject: [PATCH 063/103] NFSD: Add a cache for fs_locations information Signed-off-by: Trond Myklebust [ cel: since this is server-side, use nfsd4_ prefix instead of nfs4_ prefix. ] [ cel: implement S_ISVTX filter in bfields-normal form ] Signed-off-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsd.h | 7 +++++++ fs/nfsd/vfs.c | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 8da03e16ab35..58134a23fdfb 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -361,6 +361,13 @@ static inline u32 nfsd_suppattrs2(u32 minorversion) #define NFSD_SUPPATTR_EXCLCREAT_WORD2 \ NFSD_WRITEABLE_ATTRS_WORD2 +extern int nfsd4_is_junction(struct dentry *dentry); +#else +static inline int nfsd4_is_junction(struct dentry *dentry) +{ + return 0; +} + #endif /* CONFIG_NFSD_V4 */ #endif /* LINUX_NFSD_NFSD_H */ diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 75c35fa46155..4dd91283d039 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -168,6 +168,8 @@ int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp) { if (d_mountpoint(dentry)) return 1; + if (nfsd4_is_junction(dentry)) + return 1; if (!(exp->ex_flags & NFSEXP_V4ROOT)) return 0; return dentry->d_inode != NULL; @@ -592,6 +594,22 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac return error; } +#define NFSD_XATTR_JUNCTION_PREFIX XATTR_TRUSTED_PREFIX "junction." +#define NFSD_XATTR_JUNCTION_TYPE NFSD_XATTR_JUNCTION_PREFIX "type" +int nfsd4_is_junction(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + + if (inode == NULL) + return 0; + if (inode->i_mode & S_IXUGO) + return 0; + if (!(inode->i_mode & S_ISVTX)) + return 0; + if (vfs_getxattr(dentry, NFSD_XATTR_JUNCTION_TYPE, NULL, 0) <= 0) + return 0; + return 1; +} #endif /* defined(CONFIG_NFSD_V4) */ #ifdef CONFIG_NFSD_V3 From 849a1cf13d4394d398d91752166e92e9ecd64f8d Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Tue, 30 Aug 2011 17:18:41 +0800 Subject: [PATCH 064/103] SUNRPC: Replace svc_addr_u by sockaddr_storage For IPv6 local address, lockd can not callback to client for missing scope id when binding address at inet6_bind: 324 if (addr_type & IPV6_ADDR_LINKLOCAL) { 325 if (addr_len >= sizeof(struct sockaddr_in6) && 326 addr->sin6_scope_id) { 327 /* Override any existing binding, if another one 328 * is supplied by user. 329 */ 330 sk->sk_bound_dev_if = addr->sin6_scope_id; 331 } 332 333 /* Binding to link-local address requires an interface */ 334 if (!sk->sk_bound_dev_if) { 335 err = -EINVAL; 336 goto out_unlock; 337 } Replacing svc_addr_u by sockaddr_storage, let rqstp->rq_daddr contains more info besides address. Reviewed-by: Jeff Layton Reviewed-by: Chuck Lever Signed-off-by: Mi Jinlong Signed-off-by: J. Bruce Fields --- fs/lockd/host.c | 25 ++----------------------- fs/nfsd/nfs4state.c | 16 +--------------- include/linux/sunrpc/svc.h | 30 +++++++++++++++++++++--------- net/sunrpc/svc_xprt.c | 13 ++----------- net/sunrpc/svcsock.c | 23 +++++++++++++++++------ 5 files changed, 43 insertions(+), 64 deletions(-) diff --git a/fs/lockd/host.c b/fs/lockd/host.c index b7c99bfb3da6..6f29836ec0cb 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -316,14 +316,8 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, struct hlist_node *pos; struct nlm_host *host = NULL; struct nsm_handle *nsm = NULL; - struct sockaddr_in sin = { - .sin_family = AF_INET, - }; - struct sockaddr_in6 sin6 = { - .sin6_family = AF_INET6, - }; - struct sockaddr *src_sap; - size_t src_len = rqstp->rq_addrlen; + struct sockaddr *src_sap = svc_daddr(rqstp); + size_t src_len = rqstp->rq_daddrlen; struct nlm_lookup_host_info ni = { .server = 1, .sap = svc_addr(rqstp), @@ -340,21 +334,6 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, mutex_lock(&nlm_host_mutex); - switch (ni.sap->sa_family) { - case AF_INET: - sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr; - src_sap = (struct sockaddr *)&sin; - break; - case AF_INET6: - ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6); - src_sap = (struct sockaddr *)&sin6; - break; - default: - dprintk("lockd: %s failed; unrecognized address family\n", - __func__); - goto out; - } - if (time_after_eq(jiffies, next_gc)) nlm_gc_hosts(); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0cd346477f29..e7f83bd9b4a8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1257,20 +1257,6 @@ find_unconfirmed_client_by_str(const char *dname, unsigned int hashval) return NULL; } -static void rpc_svcaddr2sockaddr(struct sockaddr *sa, unsigned short family, union svc_addr_u *svcaddr) -{ - switch (family) { - case AF_INET: - ((struct sockaddr_in *)sa)->sin_family = AF_INET; - ((struct sockaddr_in *)sa)->sin_addr = svcaddr->addr; - return; - case AF_INET6: - ((struct sockaddr_in6 *)sa)->sin6_family = AF_INET6; - ((struct sockaddr_in6 *)sa)->sin6_addr = svcaddr->addr6; - return; - } -} - static void gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_rqst *rqstp) { @@ -1302,7 +1288,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_r conn->cb_prog = se->se_callback_prog; conn->cb_ident = se->se_callback_ident; - rpc_svcaddr2sockaddr((struct sockaddr *)&conn->cb_saddr, expected_family, &rqstp->rq_daddr); + memcpy(&conn->cb_saddr, &rqstp->rq_daddr, rqstp->rq_daddrlen); return; out_err: conn->cb_addr.ss_family = AF_UNSPEC; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index a78a51e93373..d8d5d93071b3 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -212,11 +212,6 @@ static inline void svc_putu32(struct kvec *iov, __be32 val) iov->iov_len += sizeof(__be32); } -union svc_addr_u { - struct in_addr addr; - struct in6_addr addr6; -}; - /* * The context of a single thread, including the request currently being * processed. @@ -225,8 +220,12 @@ struct svc_rqst { struct list_head rq_list; /* idle list */ struct list_head rq_all; /* all threads list */ struct svc_xprt * rq_xprt; /* transport ptr */ + struct sockaddr_storage rq_addr; /* peer address */ size_t rq_addrlen; + struct sockaddr_storage rq_daddr; /* dest addr of request + * - reply from here */ + size_t rq_daddrlen; struct svc_serv * rq_server; /* RPC service definition */ struct svc_pool * rq_pool; /* thread pool */ @@ -255,9 +254,6 @@ struct svc_rqst { unsigned short rq_secure : 1; /* secure port */ - union svc_addr_u rq_daddr; /* dest addr of request - * - reply from here */ - void * rq_argp; /* decoded arguments */ void * rq_resp; /* xdr'd results */ void * rq_auth_data; /* flavor-specific data */ @@ -300,6 +296,21 @@ static inline struct sockaddr *svc_addr(const struct svc_rqst *rqst) return (struct sockaddr *) &rqst->rq_addr; } +static inline struct sockaddr_in *svc_daddr_in(const struct svc_rqst *rqst) +{ + return (struct sockaddr_in *) &rqst->rq_daddr; +} + +static inline struct sockaddr_in6 *svc_daddr_in6(const struct svc_rqst *rqst) +{ + return (struct sockaddr_in6 *) &rqst->rq_daddr; +} + +static inline struct sockaddr *svc_daddr(const struct svc_rqst *rqst) +{ + return (struct sockaddr *) &rqst->rq_daddr; +} + /* * Check buffer bounds after decoding arguments */ @@ -340,7 +351,8 @@ struct svc_deferred_req { struct svc_xprt *xprt; struct sockaddr_storage addr; /* where reply must go */ size_t addrlen; - union svc_addr_u daddr; /* where reply must come from */ + struct sockaddr_storage daddr; /* where reply must come from */ + size_t daddrlen; struct cache_deferred_req handle; size_t xprt_hlen; int argslen; diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index bd31208bbb61..d86bb673e1f6 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -254,8 +254,6 @@ EXPORT_SYMBOL_GPL(svc_create_xprt); */ void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt) { - struct sockaddr *sin; - memcpy(&rqstp->rq_addr, &xprt->xpt_remote, xprt->xpt_remotelen); rqstp->rq_addrlen = xprt->xpt_remotelen; @@ -263,15 +261,8 @@ void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt) * Destination address in request is needed for binding the * source address in RPC replies/callbacks later. */ - sin = (struct sockaddr *)&xprt->xpt_local; - switch (sin->sa_family) { - case AF_INET: - rqstp->rq_daddr.addr = ((struct sockaddr_in *)sin)->sin_addr; - break; - case AF_INET6: - rqstp->rq_daddr.addr6 = ((struct sockaddr_in6 *)sin)->sin6_addr; - break; - } + memcpy(&rqstp->rq_daddr, &xprt->xpt_local, xprt->xpt_locallen); + rqstp->rq_daddrlen = xprt->xpt_locallen; } EXPORT_SYMBOL_GPL(svc_xprt_copy_addrs); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 767d494de7a2..dfd686eb0b7f 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -143,19 +143,20 @@ static void svc_set_cmsg_data(struct svc_rqst *rqstp, struct cmsghdr *cmh) cmh->cmsg_level = SOL_IP; cmh->cmsg_type = IP_PKTINFO; pki->ipi_ifindex = 0; - pki->ipi_spec_dst.s_addr = rqstp->rq_daddr.addr.s_addr; + pki->ipi_spec_dst.s_addr = + svc_daddr_in(rqstp)->sin_addr.s_addr; cmh->cmsg_len = CMSG_LEN(sizeof(*pki)); } break; case AF_INET6: { struct in6_pktinfo *pki = CMSG_DATA(cmh); + struct sockaddr_in6 *daddr = svc_daddr_in6(rqstp); cmh->cmsg_level = SOL_IPV6; cmh->cmsg_type = IPV6_PKTINFO; - pki->ipi6_ifindex = 0; - ipv6_addr_copy(&pki->ipi6_addr, - &rqstp->rq_daddr.addr6); + pki->ipi6_ifindex = daddr->sin6_scope_id; + ipv6_addr_copy(&pki->ipi6_addr, &daddr->sin6_addr); cmh->cmsg_len = CMSG_LEN(sizeof(*pki)); } break; @@ -498,9 +499,13 @@ static int svc_udp_get_dest_address4(struct svc_rqst *rqstp, struct cmsghdr *cmh) { struct in_pktinfo *pki = CMSG_DATA(cmh); + struct sockaddr_in *daddr = svc_daddr_in(rqstp); + if (cmh->cmsg_type != IP_PKTINFO) return 0; - rqstp->rq_daddr.addr.s_addr = pki->ipi_spec_dst.s_addr; + + daddr->sin_family = AF_INET; + daddr->sin_addr.s_addr = pki->ipi_spec_dst.s_addr; return 1; } @@ -511,9 +516,14 @@ static int svc_udp_get_dest_address6(struct svc_rqst *rqstp, struct cmsghdr *cmh) { struct in6_pktinfo *pki = CMSG_DATA(cmh); + struct sockaddr_in6 *daddr = svc_daddr_in6(rqstp); + if (cmh->cmsg_type != IPV6_PKTINFO) return 0; - ipv6_addr_copy(&rqstp->rq_daddr.addr6, &pki->ipi6_addr); + + daddr->sin6_family = AF_INET6; + ipv6_addr_copy(&daddr->sin6_addr, &pki->ipi6_addr); + daddr->sin6_scope_id = pki->ipi6_ifindex; return 1; } @@ -614,6 +624,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) skb_free_datagram_locked(svsk->sk_sk, skb); return 0; } + rqstp->rq_daddrlen = svc_addr_len(svc_daddr(rqstp)); if (skb_is_nonlinear(skb)) { /* we have to copy */ From 038c01598e728cda5b2996c4bf883e8485b2fe50 Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Tue, 30 Aug 2011 17:22:49 +0800 Subject: [PATCH 065/103] SUNRPC: compare scopeid for link-local addresses For ipv6 link-local addresses, sunrpc do not compare those scope id. This patch let sunrpc compares scope id only on link-local addresses. Signed-off-by: Mi Jinlong Reviewed-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/clnt.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index db7bcaf7c5bd..ee1bb67f525e 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -218,7 +218,13 @@ static inline bool __rpc_cmp_addr6(const struct sockaddr *sap1, { const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sap1; const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sap2; - return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr); + + if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr)) + return false; + else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL) + return sin1->sin6_scope_id == sin2->sin6_scope_id; + + return true; } static inline bool __rpc_copy_addr6(struct sockaddr *dst, From 58e7b33a58d0cd07c9294d5161553b204c75662d Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Sun, 28 Aug 2011 18:18:56 +0800 Subject: [PATCH 066/103] nfsd41: try to check reply size before operation For checking the size of reply before calling a operation, we need try to get maxsize of the operation's reply. v3: using new method as Bruce said, "we could handle operations in two different ways: - For operations that actually change something (write, rename, open, close, ...), do it the way we're doing it now: be very careful to estimate the size of the response before even processing the operation. - For operations that don't change anything (read, getattr, ...) just go ahead and do the operation. If you realize after the fact that the response is too large, then return the error at that point. So we'd add another flag to op_flags: say, OP_MODIFIES_SOMETHING. And for operations with OP_MODIFIES_SOMETHING set, we'd do the first thing. For operations without it set, we'd do the second." Signed-off-by: Mi Jinlong [bfields@redhat.com: crash, don't attempt to handle, undefined op_rsize_bop] Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 247 +++++++++++++++++++++++++++++++++++++++++---- fs/nfsd/nfs4xdr.c | 37 ++++--- fs/nfsd/xdr4.h | 1 + 3 files changed, 248 insertions(+), 37 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 460eeb329d81..752a367e0e3d 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -35,6 +35,7 @@ #include #include +#include "idmap.h" #include "cache.h" #include "xdr4.h" #include "vfs.h" @@ -1003,6 +1004,8 @@ static inline void nfsd4_increment_op_stats(u32 opnum) typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, void *); +typedef u32(*nfsd4op_rsize)(struct svc_rqst *, struct nfsd4_op *op); + enum nfsd4_op_flags { ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */ ALLOWED_ON_ABSENT_FS = 1 << 1, /* ops processed on absent fs */ @@ -1010,6 +1013,7 @@ enum nfsd4_op_flags { /* For rfc 5661 section 2.6.3.1.1: */ OP_HANDLES_WRONGSEC = 1 << 3, OP_IS_PUTFH_LIKE = 1 << 4, + OP_MODIFIES_SOMETHING = 1 << 5, /* op is non-idempotent */ }; struct nfsd4_operation { @@ -1025,6 +1029,8 @@ struct nfsd4_operation { * the v4.0 case). */ bool op_cacheresult; + /* Try to get response size before operation */ + nfsd4op_rsize op_rsize_bop; }; static struct nfsd4_operation nfsd4_ops[]; @@ -1119,6 +1125,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, struct nfsd4_operation *opdesc; struct nfsd4_compound_state *cstate = &resp->cstate; int slack_bytes; + u32 plen = 0; __be32 status; resp->xbuf = &rqstp->rq_res; @@ -1197,6 +1204,15 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, goto encode_op; } + /* If op is non-idempotent */ + if (opdesc->op_flags & OP_MODIFIES_SOMETHING) { + plen = opdesc->op_rsize_bop(rqstp, op); + op->status = nfsd4_check_resp_size(resp, plen); + } + + if (op->status) + goto encode_op; + if (opdesc->op_func) op->status = opdesc->op_func(rqstp, cstate, &op->u); else @@ -1247,6 +1263,144 @@ out: return status; } +#define op_encode_hdr_size (2) +#define op_encode_stateid_maxsz (XDR_QUADLEN(NFS4_STATEID_SIZE)) +#define op_encode_verifier_maxsz (XDR_QUADLEN(NFS4_VERIFIER_SIZE)) +#define op_encode_change_info_maxsz (5) +#define nfs4_fattr_bitmap_maxsz (4) + +#define op_encode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) +#define op_encode_lock_denied_maxsz (8 + op_encode_lockowner_maxsz) + +#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) + +#define op_encode_ace_maxsz (3 + nfs4_owner_maxsz) +#define op_encode_delegation_maxsz (1 + op_encode_stateid_maxsz + 1 + \ + op_encode_ace_maxsz) + +#define op_encode_channel_attrs_maxsz (6 + 1 + 1) + +static inline u32 nfsd4_only_status_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size) * sizeof(__be32); +} + +static inline u32 nfsd4_status_stateid_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_stateid_maxsz)* sizeof(__be32); +} + +static inline u32 nfsd4_commit_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32); +} + +static inline u32 nfsd4_create_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_change_info_maxsz + + nfs4_fattr_bitmap_maxsz) * sizeof(__be32); +} + +static inline u32 nfsd4_link_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_change_info_maxsz) + * sizeof(__be32); +} + +static inline u32 nfsd4_lock_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_lock_denied_maxsz) + * sizeof(__be32); +} + +static inline u32 nfsd4_open_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_stateid_maxsz + + op_encode_change_info_maxsz + 1 + + nfs4_fattr_bitmap_maxsz + + op_encode_delegation_maxsz) * sizeof(__be32); +} + +static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + u32 maxcount = 0, rlen = 0; + + maxcount = svc_max_payload(rqstp); + rlen = op->u.read.rd_length; + + if (rlen > maxcount) + rlen = maxcount; + + return (op_encode_hdr_size + 2) * sizeof(__be32) + rlen; +} + +static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + u32 rlen = op->u.readdir.rd_maxcount; + + if (rlen > PAGE_SIZE) + rlen = PAGE_SIZE; + + return (op_encode_hdr_size + op_encode_verifier_maxsz) + * sizeof(__be32) + rlen; +} + +static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_change_info_maxsz) + * sizeof(__be32); +} + +static inline u32 nfsd4_rename_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_change_info_maxsz + + op_encode_change_info_maxsz) * sizeof(__be32); +} + +static inline u32 nfsd4_setattr_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + nfs4_fattr_bitmap_maxsz) * sizeof(__be32); +} + +static inline u32 nfsd4_setclientid_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + 2 + 1024) * sizeof(__be32); +} + +static inline u32 nfsd4_write_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32); +} + +static inline u32 nfsd4_exchange_id_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + 2 + 1 + /* eir_clientid, eir_sequenceid */\ + 1 + 1 + 0 + /* eir_flags, spr_how, SP4_NONE (for now) */\ + 2 + /*eir_server_owner.so_minor_id */\ + /* eir_server_owner.so_major_id<> */\ + XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 +\ + /* eir_server_scope<> */\ + XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 +\ + 1 + /* eir_server_impl_id array length */\ + 0 /* ignored eir_server_impl_id contents */) * sizeof(__be32); +} + +static inline u32 nfsd4_bind_conn_to_session_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + \ + XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + /* bctsr_sessid */\ + 2 /* bctsr_dir, use_conn_in_rdma_mode */) * sizeof(__be32); +} + +static inline u32 nfsd4_create_session_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + return (op_encode_hdr_size + \ + XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + /* sessionid */\ + 2 + /* csr_sequence, csr_flags */\ + op_encode_channel_attrs_maxsz + \ + op_encode_channel_attrs_maxsz) * sizeof(__be32); +} + static struct nfsd4_operation nfsd4_ops[] = { [OP_ACCESS] = { .op_func = (nfsd4op_func)nfsd4_access, @@ -1254,20 +1408,28 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_CLOSE] = { .op_func = (nfsd4op_func)nfsd4_close, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_CLOSE", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, }, [OP_COMMIT] = { .op_func = (nfsd4op_func)nfsd4_commit, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_COMMIT", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_commit_rsize, }, [OP_CREATE] = { .op_func = (nfsd4op_func)nfsd4_create, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_CREATE", .op_cacheresult = true, + .op_rsize_bop = (nfsd4op_rsize)nfsd4_create_rsize, }, [OP_DELEGRETURN] = { .op_func = (nfsd4op_func)nfsd4_delegreturn, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_DELEGRETURN", + .op_rsize_bop = nfsd4_only_status_rsize, }, [OP_GETATTR] = { .op_func = (nfsd4op_func)nfsd4_getattr, @@ -1280,12 +1442,16 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_LINK] = { .op_func = (nfsd4op_func)nfsd4_link, + .op_flags = ALLOWED_ON_ABSENT_FS | OP_MODIFIES_SOMETHING, .op_name = "OP_LINK", .op_cacheresult = true, + .op_rsize_bop = (nfsd4op_rsize)nfsd4_link_rsize, }, [OP_LOCK] = { .op_func = (nfsd4op_func)nfsd4_lock, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_LOCK", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_lock_rsize, }, [OP_LOCKT] = { .op_func = (nfsd4op_func)nfsd4_lockt, @@ -1293,7 +1459,9 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_LOCKU] = { .op_func = (nfsd4op_func)nfsd4_locku, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_LOCKU", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, }, [OP_LOOKUP] = { .op_func = (nfsd4op_func)nfsd4_lookup, @@ -1311,42 +1479,54 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_OPEN] = { .op_func = (nfsd4op_func)nfsd4_open, - .op_flags = OP_HANDLES_WRONGSEC, + .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING, .op_name = "OP_OPEN", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_open_rsize, }, [OP_OPEN_CONFIRM] = { .op_func = (nfsd4op_func)nfsd4_open_confirm, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_OPEN_CONFIRM", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, }, [OP_OPEN_DOWNGRADE] = { .op_func = (nfsd4op_func)nfsd4_open_downgrade, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_OPEN_DOWNGRADE", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, }, [OP_PUTFH] = { .op_func = (nfsd4op_func)nfsd4_putfh, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS - | OP_IS_PUTFH_LIKE, + | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING, .op_name = "OP_PUTFH", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_PUTPUBFH] = { .op_func = (nfsd4op_func)nfsd4_putrootfh, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS - | OP_IS_PUTFH_LIKE, + | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING, .op_name = "OP_PUTPUBFH", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_PUTROOTFH] = { .op_func = (nfsd4op_func)nfsd4_putrootfh, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS - | OP_IS_PUTFH_LIKE, + | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING, .op_name = "OP_PUTROOTFH", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_READ] = { .op_func = (nfsd4op_func)nfsd4_read, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_READ", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize, }, [OP_READDIR] = { .op_func = (nfsd4op_func)nfsd4_readdir, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_READDIR", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_readdir_rsize, }, [OP_READLINK] = { .op_func = (nfsd4op_func)nfsd4_readlink, @@ -1354,29 +1534,38 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_REMOVE] = { .op_func = (nfsd4op_func)nfsd4_remove, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_REMOVE", .op_cacheresult = true, + .op_rsize_bop = (nfsd4op_rsize)nfsd4_remove_rsize, }, [OP_RENAME] = { - .op_name = "OP_RENAME", .op_func = (nfsd4op_func)nfsd4_rename, + .op_flags = OP_MODIFIES_SOMETHING, + .op_name = "OP_RENAME", .op_cacheresult = true, + .op_rsize_bop = (nfsd4op_rsize)nfsd4_rename_rsize, }, [OP_RENEW] = { .op_func = (nfsd4op_func)nfsd4_renew, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS + | OP_MODIFIES_SOMETHING, .op_name = "OP_RENEW", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, + }, [OP_RESTOREFH] = { .op_func = (nfsd4op_func)nfsd4_restorefh, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS - | OP_IS_PUTFH_LIKE, + | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING, .op_name = "OP_RESTOREFH", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_SAVEFH] = { .op_func = (nfsd4op_func)nfsd4_savefh, - .op_flags = OP_HANDLES_WRONGSEC, + .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING, .op_name = "OP_SAVEFH", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_SECINFO] = { .op_func = (nfsd4op_func)nfsd4_secinfo, @@ -1386,19 +1575,25 @@ static struct nfsd4_operation nfsd4_ops[] = { [OP_SETATTR] = { .op_func = (nfsd4op_func)nfsd4_setattr, .op_name = "OP_SETATTR", + .op_flags = OP_MODIFIES_SOMETHING, .op_cacheresult = true, + .op_rsize_bop = (nfsd4op_rsize)nfsd4_setattr_rsize, }, [OP_SETCLIENTID] = { .op_func = (nfsd4op_func)nfsd4_setclientid, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS + | OP_MODIFIES_SOMETHING, .op_name = "OP_SETCLIENTID", .op_cacheresult = true, + .op_rsize_bop = (nfsd4op_rsize)nfsd4_setclientid_rsize, }, [OP_SETCLIENTID_CONFIRM] = { .op_func = (nfsd4op_func)nfsd4_setclientid_confirm, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS + | OP_MODIFIES_SOMETHING, .op_name = "OP_SETCLIENTID_CONFIRM", .op_cacheresult = true, + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_VERIFY] = { .op_func = (nfsd4op_func)nfsd4_verify, @@ -1406,35 +1601,47 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_WRITE] = { .op_func = (nfsd4op_func)nfsd4_write, + .op_flags = OP_MODIFIES_SOMETHING, .op_name = "OP_WRITE", .op_cacheresult = true, + .op_rsize_bop = (nfsd4op_rsize)nfsd4_write_rsize, }, [OP_RELEASE_LOCKOWNER] = { .op_func = (nfsd4op_func)nfsd4_release_lockowner, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS + | OP_MODIFIES_SOMETHING, .op_name = "OP_RELEASE_LOCKOWNER", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, /* NFSv4.1 operations */ [OP_EXCHANGE_ID] = { .op_func = (nfsd4op_func)nfsd4_exchange_id, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP + | OP_MODIFIES_SOMETHING, .op_name = "OP_EXCHANGE_ID", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_exchange_id_rsize, }, [OP_BIND_CONN_TO_SESSION] = { .op_func = (nfsd4op_func)nfsd4_bind_conn_to_session, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP + | OP_MODIFIES_SOMETHING, .op_name = "OP_BIND_CONN_TO_SESSION", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_bind_conn_to_session_rsize, }, [OP_CREATE_SESSION] = { .op_func = (nfsd4op_func)nfsd4_create_session, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP + | OP_MODIFIES_SOMETHING, .op_name = "OP_CREATE_SESSION", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_create_session_rsize, }, [OP_DESTROY_SESSION] = { .op_func = (nfsd4op_func)nfsd4_destroy_session, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP + | OP_MODIFIES_SOMETHING, .op_name = "OP_DESTROY_SESSION", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_SEQUENCE] = { .op_func = (nfsd4op_func)nfsd4_sequence, @@ -1443,13 +1650,16 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_DESTROY_CLIENTID] = { .op_func = NULL, - .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP + | OP_MODIFIES_SOMETHING, .op_name = "OP_DESTROY_CLIENTID", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_RECLAIM_COMPLETE] = { .op_func = (nfsd4op_func)nfsd4_reclaim_complete, - .op_flags = ALLOWED_WITHOUT_FH, + .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, .op_name = "OP_RECLAIM_COMPLETE", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_SECINFO_NO_NAME] = { .op_func = (nfsd4op_func)nfsd4_secinfo_no_name, @@ -1463,8 +1673,9 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_FREE_STATEID] = { .op_func = (nfsd4op_func)nfsd4_free_stateid, - .op_flags = ALLOWED_WITHOUT_FH, + .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, .op_name = "OP_FREE_STATEID", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, }; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 5252d6681960..f4116cf16595 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3387,34 +3387,29 @@ static nfsd4_enc nfsd4_enc_ops[] = { /* * Calculate the total amount of memory that the compound response has taken - * after encoding the current operation. + * after encoding the current operation with pad. * - * pad: add on 8 bytes for the next operation's op_code and status so that - * there is room to cache a failure on the next operation. + * pad: if operation is non-idempotent, pad was calculate by op_rsize_bop() + * which was specified at nfsd4_operation, else pad is zero. * - * Compare this length to the session se_fmaxresp_cached. + * Compare this length to the session se_fmaxresp_sz and se_fmaxresp_cached. * * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so * will be at least a page and will therefore hold the xdr_buf head. */ -static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp) +int nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad) { - int status = 0; struct xdr_buf *xb = &resp->rqstp->rq_res; - struct nfsd4_compoundargs *args = resp->rqstp->rq_argp; struct nfsd4_session *session = NULL; struct nfsd4_slot *slot = resp->cstate.slot; - u32 length, tlen = 0, pad = 8; + u32 length, tlen = 0; if (!nfsd4_has_session(&resp->cstate)) - return status; + return 0; session = resp->cstate.session; - if (session == NULL || slot->sl_cachethis == 0) - return status; - - if (resp->opcnt >= args->opcnt) - pad = 0; /* this is the last operation */ + if (session == NULL) + return 0; if (xb->page_len == 0) { length = (char *)resp->p - (char *)xb->head[0].iov_base + pad; @@ -3427,10 +3422,14 @@ static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp) dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__, length, xb->page_len, tlen, pad); - if (length <= session->se_fchannel.maxresp_cached) - return status; - else + if (length > session->se_fchannel.maxresp_sz) + return nfserr_rep_too_big; + + if (slot->sl_cachethis == 1 && + length > session->se_fchannel.maxresp_cached) return nfserr_rep_too_big_to_cache; + + return 0; } void @@ -3450,8 +3449,8 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) !nfsd4_enc_ops[op->opnum]); op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u); /* nfsd4_check_drc_limit guarantees enough room for error status */ - if (!op->status && nfsd4_check_drc_limit(resp)) - op->status = nfserr_rep_too_big_to_cache; + if (!op->status) + op->status = nfsd4_check_resp_size(resp, 0); status: /* * Note: We write the status directly, instead of using WRITE32(), diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index f95a72482064..a767b57b8208 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -524,6 +524,7 @@ int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *, struct nfsd4_compoundargs *); int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *, struct nfsd4_compoundres *); +int nfsd4_check_resp_size(struct nfsd4_compoundres *, u32); void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *); void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op); __be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, From dad1c067eb42ec8bedadd64f681056914547d22e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 12 Sep 2011 12:24:13 -0400 Subject: [PATCH 067/103] nfsd4: replace oo_confirmed by flag bit I want at least one more bit here. So, let's haul out the caps lock key and add a flags field. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 4 ++-- fs/nfsd/nfs4state.c | 24 ++++++++++++------------ fs/nfsd/state.h | 3 ++- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 752a367e0e3d..aa769dfa756a 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -375,7 +375,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; break; case NFS4_OPEN_CLAIM_PREVIOUS: - open->op_openowner->oo_confirmed = 1; + open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; /* * The CURRENT_FH is already set to the file being * opened. (1) set open->op_cinfo, (2) set @@ -388,7 +388,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; break; case NFS4_OPEN_CLAIM_DELEGATE_PREV: - open->op_openowner->oo_confirmed = 1; + open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; dprintk("NFSD: unsupported OPEN claim type %d\n", open->op_claim_type); status = nfserr_notsupp; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e7f83bd9b4a8..59b70afdc884 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2322,7 +2322,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str return NULL; oo->oo_owner.so_is_open_owner = 1; oo->oo_owner.so_seqid = open->op_seqid; - oo->oo_confirmed = 0; + oo->oo_flags = 0; oo->oo_time = 0; INIT_LIST_HEAD(&oo->oo_close_lru); hash_openowner(oo, clp, strhashval); @@ -2549,7 +2549,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, return nfserr_expired; goto renew; } - if (!oo->oo_confirmed) { + if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) { /* Replace unconfirmed owners without checking for replay. */ clp = oo->oo_owner.so_client; release_openowner(oo); @@ -2616,7 +2616,7 @@ out: return nfs_ok; if (status) return status; - open->op_openowner->oo_confirmed = 1; + open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; return nfs_ok; } @@ -2749,7 +2749,7 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c static void nfs4_set_claim_prev(struct nfsd4_open *open) { - open->op_openowner->oo_confirmed = 1; + open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; open->op_openowner->oo_owner.so_client->cl_firststate = 1; } @@ -2853,7 +2853,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_ * had the chance to reclaim theirs.... */ if (locks_in_grace()) goto out; - if (!cb_up || !oo->oo_confirmed) + if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED)) goto out; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) flag = NFS4_OPEN_DELEGATE_WRITE; @@ -2952,7 +2952,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); if (nfsd4_has_session(&resp->cstate)) - open->op_openowner->oo_confirmed = 1; + open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; /* * Attempt to hand out a delegation. No error return, because the @@ -2973,7 +2973,7 @@ out: * To finish the open response, we just need to set the rflags. */ open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX; - if (!open->op_openowner->oo_confirmed && + if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED) && !nfsd4_has_session(&resp->cstate)) open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM; @@ -3264,7 +3264,7 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) return nfs_ok; ols = openlockstateid(s); if (ols->st_stateowner->so_is_open_owner - && !openowner(ols->st_stateowner)->oo_confirmed) + && !(openowner(ols->st_stateowner)->oo_flags & NFS4_OO_CONFIRMED)) return nfserr_bad_stateid; return nfs_ok; } @@ -3323,7 +3323,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, if (nfs4_check_fh(current_fh, stp)) goto out; if (stp->st_stateowner->so_is_open_owner - && !openowner(stp->st_stateowner)->oo_confirmed) + && !(openowner(stp->st_stateowner)->oo_flags & NFS4_OO_CONFIRMED)) goto out; status = nfs4_check_openmode(stp, flags); if (status) @@ -3476,7 +3476,7 @@ static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cs if (status) return status; oo = openowner((*stpp)->st_stateowner); - if (!oo->oo_confirmed) + if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) return nfserr_bad_stateid; return nfs_ok; } @@ -3506,9 +3506,9 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; oo = openowner(stp->st_stateowner); status = nfserr_bad_stateid; - if (oo->oo_confirmed) + if (oo->oo_flags & NFS4_OO_CONFIRMED) goto out; - oo->oo_confirmed = 1; + oo->oo_flags |= NFS4_OO_CONFIRMED; update_stateid(&stp->st_stid.sc_stateid); memcpy(&oc->oc_resp_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 12c142436705..a8324b868a36 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -371,7 +371,8 @@ struct nfs4_openowner { struct list_head oo_perclient; struct list_head oo_close_lru; /* tail queue */ time_t oo_time; /* time of placement on so_close_lru */ - int oo_confirmed; /* successful OPEN_CONFIRM? */ +#define NFS4_OO_CONFIRMED 1 + unsigned char oo_flags; }; struct nfs4_lockowner { From 38c387b52d8404f8fd29d8c26bebc83a80733657 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 16 Sep 2011 17:42:48 -0400 Subject: [PATCH 068/103] nfsd4: match close replays on stateid, not open owner id Keep around an unhashed copy of the final stateid after the last close using an openowner, and when identifying a replay, match against that stateid instead of just against the open owner id. Free it the next time the seqid is bumped or the stateowner is destroyed. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 47 ++++++++++++++++++++++++++++++++++++++------- fs/nfsd/nfs4xdr.c | 1 + fs/nfsd/state.h | 3 +++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 59b70afdc884..a174841b262e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -438,7 +438,6 @@ static void close_generic_stateid(struct nfs4_ol_stateid *stp) static void free_generic_stateid(struct nfs4_ol_stateid *stp) { - close_generic_stateid(stp); kmem_cache_free(stateid_slab, stp); } @@ -450,6 +449,7 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp) file = find_any_file(stp->st_file); if (file) locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner)); + close_generic_stateid(stp); free_generic_stateid(stp); } @@ -485,10 +485,16 @@ release_stateid_lockowners(struct nfs4_ol_stateid *open_stp) } } -static void release_open_stateid(struct nfs4_ol_stateid *stp) +static void unhash_open_stateid(struct nfs4_ol_stateid *stp) { unhash_generic_stateid(stp); release_stateid_lockowners(stp); + close_generic_stateid(stp); +} + +static void release_open_stateid(struct nfs4_ol_stateid *stp) +{ + unhash_open_stateid(stp); free_generic_stateid(stp); } @@ -510,6 +516,8 @@ static void release_openowner(struct nfs4_openowner *oo) { unhash_openowner(oo); list_del(&oo->oo_close_lru); + if (oo->oo_last_closed_stid) + free_generic_stateid(oo->oo_last_closed_stid); nfs4_free_openowner(oo); } @@ -2324,6 +2332,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str oo->oo_owner.so_seqid = open->op_seqid; oo->oo_flags = 0; oo->oo_time = 0; + oo->oo_last_closed_stid = NULL; INIT_LIST_HEAD(&oo->oo_close_lru); hash_openowner(oo, clp, strhashval); return oo; @@ -3120,12 +3129,14 @@ laundromat_main(struct work_struct *not_used) queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); } -static struct nfs4_openowner * search_close_lru(u32 st_id) +static struct nfs4_openowner * search_close_lru(stateid_t *s) { struct nfs4_openowner *local; + struct nfs4_ol_stateid *os; list_for_each_entry(local, &close_lru, oo_close_lru) { - if (local->oo_owner.so_id == st_id) + os = local->oo_last_closed_stid; + if (same_stateid(&os->st_stid.sc_stateid, s)) return local; } return NULL; @@ -3589,6 +3600,27 @@ out: return status; } +void nfsd4_purge_closed_stateid(struct nfs4_stateowner *so) +{ + struct nfs4_openowner *oo; + struct nfs4_ol_stateid *s; + + if (!so->so_is_open_owner) + return; + oo = openowner(so); + s = oo->oo_last_closed_stid; + if (!s) + return; + if (!(oo->oo_flags & NFS4_OO_PURGE_CLOSE)) { + /* Release the last_closed_stid on the next seqid bump: */ + oo->oo_flags |= NFS4_OO_PURGE_CLOSE; + return; + } + oo->oo_flags &= ~NFS4_OO_PURGE_CLOSE; + free_generic_stateid(oo->oo_last_closed_stid); + oo->oo_last_closed_stid = NULL; +} + /* * nfs4_unlock_state() called after encode */ @@ -3613,7 +3645,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * Also, we should make sure this isn't just the result of * a replayed close: */ - oo = search_close_lru(close->cl_stateid.si_stateownerid); + oo = search_close_lru(&close->cl_stateid); /* It's not stale; let's assume it's expired: */ if (oo == NULL) goto out; @@ -3630,8 +3662,9 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, update_stateid(&stp->st_stid.sc_stateid); memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); - /* release_stateid() calls nfsd_close() if needed */ - release_open_stateid(stp); + /* unhash_open_stateid() calls nfsd_close() if needed */ + oo->oo_last_closed_stid = stp; + unhash_open_stateid(stp); /* place unused nfs4_stateowners on so_close_lru list to be * released by the laundromat service after the lease period diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index f4116cf16595..7bd57c2dbc4d 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1636,6 +1636,7 @@ static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, _ (char *)resp->p - (char *)save; memcpy(stateowner->so_replay.rp_buf, save, stateowner->so_replay.rp_buflen); + nfsd4_purge_closed_stateid(stateowner); } } diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index a8324b868a36..e807abb116f6 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -370,8 +370,10 @@ struct nfs4_openowner { struct nfs4_stateowner oo_owner; /* must be first field */ struct list_head oo_perclient; struct list_head oo_close_lru; /* tail queue */ + struct nfs4_ol_stateid *oo_last_closed_stid; time_t oo_time; /* time of placement on so_close_lru */ #define NFS4_OO_CONFIRMED 1 +#define NFS4_OO_PURGE_CLOSE 2 unsigned char oo_flags; }; @@ -514,5 +516,6 @@ extern int nfsd4_create_clid_dir(struct nfs4_client *clp); extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); extern void release_session_client(struct nfsd4_session *); extern __be32 nfs4_validate_stateid(stateid_t *, bool); +extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); #endif /* NFSD4_STATE_H */ From 2da1cec713bc6d3ec9732e7d48b8bc0453580fd3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 16 Sep 2011 18:56:20 -0400 Subject: [PATCH 069/103] nfsd4: simplify free_stateid We no longer need is_deleg_stateid, for example. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 66 +++++++++++---------------------------------- 1 file changed, 15 insertions(+), 51 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a174841b262e..fdd03f6ae044 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1098,16 +1098,6 @@ static struct nfs4_stid *find_stateid(stateid_t *t) return NULL; } -static struct nfs4_ol_stateid *find_ol_stateid(stateid_t *t) -{ - struct nfs4_stid *s; - - s = find_stateid(t); - if (!s) - return NULL; - return openlockstateid(s); -} - static struct nfs4_stid *find_stateid_by_type(stateid_t *t, char typemask) { struct nfs4_stid *s; @@ -3251,11 +3241,6 @@ static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_sess return nfserr_old_stateid; } -static int is_delegation_stateid(stateid_t *stateid) -{ - return stateid->si_fileid == 0; -} - __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) { struct nfs4_stid *s; @@ -3352,16 +3337,6 @@ out: return status; } -static __be32 -nfsd4_free_delegation_stateid(stateid_t *stateid) -{ - struct nfs4_delegation *dp = find_deleg_stateid(stateid); - if (dp) - return nfserr_locks_held; - - return nfserr_bad_stateid; -} - static __be32 nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp) { @@ -3382,40 +3357,32 @@ nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return nfs_ok; } -/* - * Free a state id - */ __be32 nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_free_stateid *free_stateid) { stateid_t *stateid = &free_stateid->fr_stateid; - struct nfs4_ol_stateid *stp; - __be32 ret; + struct nfs4_stid *s; + __be32 ret = nfserr_bad_stateid; nfs4_lock_state(); - if (is_delegation_stateid(stateid)) { - ret = nfsd4_free_delegation_stateid(stateid); + s = find_stateid(stateid); + if (!s) goto out; - } - - stp = find_ol_stateid(stateid); - if (!stp) { - ret = nfserr_bad_stateid; - goto out; - } - ret = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, 1); - if (ret) - goto out; - - if (stp->st_stid.sc_type == NFS4_OPEN_STID) { + switch (s->sc_type) { + case NFS4_DELEG_STID: ret = nfserr_locks_held; goto out; - } else { - ret = nfsd4_free_lock_stateid(stp); - goto out; + case NFS4_OPEN_STID: + case NFS4_LOCK_STID: + ret = check_stateid_generation(stateid, &s->sc_stateid, 1); + if (ret) + goto out; + if (s->sc_type == NFS4_LOCK_STID) + ret = nfsd4_free_lock_stateid(openlockstateid(s)); + else + ret = nfserr_locks_held; } - out: nfs4_unlock_state(); return ret; @@ -3698,9 +3665,6 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_stale_stateid; if (STALE_STATEID(stateid)) goto out; - status = nfserr_bad_stateid; - if (!is_delegation_stateid(stateid)) - goto out; status = nfserr_expired; dp = find_deleg_stateid(stateid); if (!dp) From d3b313a463c64c54d57c6af09c4a5d20106c1d1c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 15 Sep 2011 15:02:41 -0400 Subject: [PATCH 070/103] nfsd4: construct stateid from clientid and counter Including the full clientid in the on-the-wire stateid allows more reliable detection of bad vs. expired stateid's, simplifies code, and ensures we won't reuse the opaque part of the stateid (as we currently do when the same openowner closes and reopens the same file). Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 58 ++++++++++----------------------------------- fs/nfsd/state.h | 18 ++++---------- 2 files changed, 18 insertions(+), 58 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index fdd03f6ae044..922f47dd0d74 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -49,9 +49,7 @@ time_t nfsd4_lease = 90; /* default lease time */ time_t nfsd4_grace = 90; static time_t boot_time; -static u32 current_ownerid = 1; -static u32 current_fileid = 1; -static u32 current_delegid = 1; +static u32 current_stateid = 1; static stateid_t zerostateid; /* bits all 0 */ static stateid_t onestateid; /* bits all 1 */ static u64 current_sessionid = 1; @@ -136,11 +134,6 @@ unsigned int max_delegations; #define OPEN_OWNER_HASH_SIZE (1 << OPEN_OWNER_HASH_BITS) #define OPEN_OWNER_HASH_MASK (OPEN_OWNER_HASH_SIZE - 1) -static unsigned int open_ownerid_hashval(const u32 id) -{ - return id & OPEN_OWNER_HASH_MASK; -} - static unsigned int open_ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) { unsigned int ret; @@ -150,7 +143,6 @@ static unsigned int open_ownerstr_hashval(u32 clientid, struct xdr_netobj *owner return ret & OPEN_OWNER_HASH_MASK; } -static struct list_head open_ownerid_hashtbl[OPEN_OWNER_HASH_SIZE]; static struct list_head open_ownerstr_hashtbl[OPEN_OWNER_HASH_SIZE]; /* hash table for nfs4_file */ @@ -255,9 +247,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv dp->dl_file = fp; dp->dl_type = type; dp->dl_stid.sc_type = NFS4_DELEG_STID; - dp->dl_stid.sc_stateid.si_boot = boot_time; - dp->dl_stid.sc_stateid.si_stateownerid = current_delegid++; - dp->dl_stid.sc_stateid.si_fileid = 0; + dp->dl_stid.sc_stateid.si_opaque.so_clid = clp->cl_clientid; + dp->dl_stid.sc_stateid.si_opaque.so_id = current_stateid++; dp->dl_stid.sc_stateid.si_generation = 1; hash_stid(&dp->dl_stid); fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); @@ -457,7 +448,6 @@ static void unhash_lockowner(struct nfs4_lockowner *lo) { struct nfs4_ol_stateid *stp; - list_del(&lo->lo_owner.so_idhash); list_del(&lo->lo_owner.so_strhash); list_del(&lo->lo_perstateid); while (!list_empty(&lo->lo_owner.so_stateids)) { @@ -502,7 +492,6 @@ static void unhash_openowner(struct nfs4_openowner *oo) { struct nfs4_ol_stateid *stp; - list_del(&oo->oo_owner.so_idhash); list_del(&oo->oo_owner.so_strhash); list_del(&oo->oo_perclient); while (!list_empty(&oo->oo_owner.so_stateids)) { @@ -1081,9 +1070,8 @@ static void gen_confirm(struct nfs4_client *clp) static int same_stateid(stateid_t *id_one, stateid_t *id_two) { - if (id_one->si_stateownerid != id_two->si_stateownerid) - return 0; - return id_one->si_fileid == id_two->si_fileid; + return 0 == memcmp(&id_one->si_opaque, &id_two->si_opaque, + sizeof(stateid_opaque_t)); } static struct nfs4_stid *find_stateid(stateid_t *t) @@ -2198,7 +2186,6 @@ alloc_init_file(struct inode *ino) INIT_LIST_HEAD(&fp->fi_stateids); INIT_LIST_HEAD(&fp->fi_delegations); fp->fi_inode = igrab(ino); - fp->fi_id = current_fileid++; fp->fi_had_conflict = false; fp->fi_lease = NULL; memset(fp->fi_fds, 0, sizeof(fp->fi_fds)); @@ -2295,7 +2282,6 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj sop->so_owner.len = owner->len; INIT_LIST_HEAD(&sop->so_stateids); - sop->so_id = current_ownerid++; sop->so_client = clp; init_nfs4_replay(&sop->so_replay); return sop; @@ -2303,10 +2289,6 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval) { - unsigned int idhashval; - - idhashval = open_ownerid_hashval(oo->oo_owner.so_id); - list_add(&oo->oo_owner.so_idhash, &open_ownerid_hashtbl[idhashval]); list_add(&oo->oo_owner.so_strhash, &open_ownerstr_hashtbl[strhashval]); list_add(&oo->oo_perclient, &clp->cl_openowners); } @@ -2331,6 +2313,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str static inline void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { struct nfs4_openowner *oo = open->op_openowner; + struct nfs4_client *clp = oo->oo_owner.so_client; INIT_LIST_HEAD(&stp->st_lockowners); list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); @@ -2339,9 +2322,8 @@ init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd stp->st_stateowner = &oo->oo_owner; get_nfs4_file(fp); stp->st_file = fp; - stp->st_stid.sc_stateid.si_boot = boot_time; - stp->st_stid.sc_stateid.si_stateownerid = oo->oo_owner.so_id; - stp->st_stid.sc_stateid.si_fileid = fp->fi_id; + stp->st_stid.sc_stateid.si_opaque.so_clid = clp->cl_clientid; + stp->st_stid.sc_stateid.si_opaque.so_id = current_stateid++; /* note will be incremented before first return to client: */ stp->st_stid.sc_stateid.si_generation = 0; hash_stid(&stp->st_stid); @@ -3095,8 +3077,6 @@ nfs4_laundromat(void) test_val = u; break; } - dprintk("NFSD: purging unused open stateowner (so_id %d)\n", - oo->oo_owner.so_id); release_openowner(oo); } if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT) @@ -3141,7 +3121,7 @@ nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp) static int STALE_STATEID(stateid_t *stateid) { - if (stateid->si_boot == boot_time) + if (stateid->si_opaque.so_clid.cl_boot == boot_time) return 0; dprintk("NFSD: stale stateid " STATEID_FMT "!\n", STATEID_VAL(stateid)); @@ -3710,11 +3690,6 @@ last_byte_offset(u64 start, u64 len) return end > start ? end - 1: NFS4_MAX_UINT64; } -static unsigned int lockownerid_hashval(u32 id) -{ - return id & LOCK_HASH_MASK; -} - static inline unsigned int lock_ownerstr_hashval(struct inode *inode, u32 cl_id, struct xdr_netobj *ownername) @@ -3724,7 +3699,6 @@ lock_ownerstr_hashval(struct inode *inode, u32 cl_id, & LOCK_HASH_MASK; } -static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE]; static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; /* @@ -3795,10 +3769,6 @@ find_lockowner_str(struct inode *inode, clientid_t *clid, static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_ol_stateid *open_stp) { - unsigned int idhashval; - - idhashval = lockownerid_hashval(lo->lo_owner.so_id); - list_add(&lo->lo_owner.so_idhash, &lock_ownerid_hashtbl[idhashval]); list_add(&lo->lo_owner.so_strhash, &lock_ownerstr_hashtbl[strhashval]); list_add(&lo->lo_perstateid, &open_stp->st_lockowners); } @@ -3831,6 +3801,7 @@ static struct nfs4_ol_stateid * alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct nfs4_ol_stateid *open_stp) { struct nfs4_ol_stateid *stp; + struct nfs4_client *clp = lo->lo_owner.so_client; stp = nfs4_alloc_stateid(); if (stp == NULL) @@ -3841,9 +3812,8 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct stp->st_stid.sc_type = NFS4_LOCK_STID; get_nfs4_file(fp); stp->st_file = fp; - stp->st_stid.sc_stateid.si_boot = boot_time; - stp->st_stid.sc_stateid.si_stateownerid = lo->lo_owner.so_id; - stp->st_stid.sc_stateid.si_fileid = fp->fi_id; + stp->st_stid.sc_stateid.si_opaque.so_clid = clp->cl_clientid; + stp->st_stid.sc_stateid.si_opaque.so_id = current_stateid++; /* note will be incremented before first return to client: */ stp->st_stid.sc_stateid.si_generation = 0; hash_stid(&stp->st_stid); @@ -4252,7 +4222,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, * data structures. */ INIT_LIST_HEAD(&matches); for (i = 0; i < LOCK_HASH_SIZE; i++) { - list_for_each_entry(sop, &lock_ownerid_hashtbl[i], so_idhash) { + list_for_each_entry(sop, &lock_ownerstr_hashtbl[i], so_strhash) { if (!same_owner_str(sop, owner, clid)) continue; list_for_each_entry(stp, &sop->so_stateids, @@ -4398,12 +4368,10 @@ nfs4_state_init(void) } for (i = 0; i < OPEN_OWNER_HASH_SIZE; i++) { INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]); - INIT_LIST_HEAD(&open_ownerid_hashtbl[i]); } for (i = 0; i < STATEID_HASH_SIZE; i++) INIT_LIST_HEAD(&stateid_hashtbl[i]); for (i = 0; i < LOCK_HASH_SIZE; i++) { - INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]); INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); } memset(&onestateid, ~0, sizeof(stateid_t)); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index e807abb116f6..d6aec4f8d3dd 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -45,24 +45,20 @@ typedef struct { } clientid_t; typedef struct { - u32 so_boot; - u32 so_stateownerid; - u32 so_fileid; + clientid_t so_clid; + u32 so_id; } stateid_opaque_t; typedef struct { u32 si_generation; stateid_opaque_t si_opaque; } stateid_t; -#define si_boot si_opaque.so_boot -#define si_stateownerid si_opaque.so_stateownerid -#define si_fileid si_opaque.so_fileid #define STATEID_FMT "(%08x/%08x/%08x/%08x)" #define STATEID_VAL(s) \ - (s)->si_boot, \ - (s)->si_stateownerid, \ - (s)->si_fileid, \ + (s)->si_opaque.so_clid.cl_boot, \ + (s)->si_opaque.so_clid.cl_id, \ + (s)->si_opaque.so_id, \ (s)->si_generation struct nfsd4_callback { @@ -353,11 +349,9 @@ struct nfs4_replay { */ struct nfs4_stateowner { - struct list_head so_idhash; /* hash by so_id */ struct list_head so_strhash; /* hash by op_name */ struct list_head so_stateids; int so_is_open_owner; /* 1=openowner,0=lockowner */ - u32 so_id; struct nfs4_client * so_client; /* after increment in ENCODE_SEQID_OP_TAIL, represents the next * sequence id expected from the client: */ @@ -415,8 +409,6 @@ struct nfs4_file { struct file_lock *fi_lease; atomic_t fi_delegees; struct inode *fi_inode; - u32 fi_id; /* used with stateowner->so_id - * for stateid_hashtbl hash */ bool fi_had_conflict; }; From f7a4d872078a5e143d88adb561627f637046b05a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 16 Sep 2011 20:12:38 -0400 Subject: [PATCH 071/103] nfsd4: hash closed stateid's like any other Look up closed stateid's in the stateid hash like any other stateid rather than searching the close lru. This is simpler, and fixes a bug: currently we handle only the case of a close that is the last close for a given stateowner, but not the case of a close for a stateowner that still has active opens on other files. Thus in a case like: open(owner, file1) open(owner, file2) close(owner, file2) close(owner, file2) the final close won't be recognized as a retransmission. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 101 +++++++++++++++++++++++--------------------- fs/nfsd/state.h | 4 +- 2 files changed, 56 insertions(+), 49 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 922f47dd0d74..e5cba833613f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -406,7 +406,6 @@ static int nfs4_access_to_omode(u32 access) static void unhash_generic_stateid(struct nfs4_ol_stateid *stp) { - list_del(&stp->st_stid.sc_hash); list_del(&stp->st_perfile); list_del(&stp->st_perstateowner); } @@ -437,6 +436,7 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp) struct file *file; unhash_generic_stateid(stp); + list_del(&stp->st_stid.sc_hash); file = find_any_file(stp->st_file); if (file) locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner)); @@ -485,6 +485,7 @@ static void unhash_open_stateid(struct nfs4_ol_stateid *stp) static void release_open_stateid(struct nfs4_ol_stateid *stp) { unhash_open_stateid(stp); + list_del(&stp->st_stid.sc_hash); free_generic_stateid(stp); } @@ -501,12 +502,22 @@ static void unhash_openowner(struct nfs4_openowner *oo) } } +static void release_last_closed_stateid(struct nfs4_openowner *oo) +{ + struct nfs4_ol_stateid *s = oo->oo_last_closed_stid; + + if (s) { + list_del_init(&s->st_stid.sc_hash); + free_generic_stateid(s); + oo->oo_last_closed_stid = NULL; + } +} + static void release_openowner(struct nfs4_openowner *oo) { unhash_openowner(oo); list_del(&oo->oo_close_lru); - if (oo->oo_last_closed_stid) - free_generic_stateid(oo->oo_last_closed_stid); + release_last_closed_stateid(oo); nfs4_free_openowner(oo); } @@ -3099,23 +3110,11 @@ laundromat_main(struct work_struct *not_used) queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); } -static struct nfs4_openowner * search_close_lru(stateid_t *s) +static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp) { - struct nfs4_openowner *local; - struct nfs4_ol_stateid *os; - - list_for_each_entry(local, &close_lru, oo_close_lru) { - os = local->oo_last_closed_stid; - if (same_stateid(&os->st_stid.sc_stateid, s)) - return local; - } - return NULL; -} - -static inline int -nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp) -{ - return fhp->fh_dentry->d_inode != stp->st_file->fi_inode; + if (fhp->fh_dentry->d_inode != stp->st_file->fi_inode) + return nfserr_bad_stateid; + return nfs_ok; } static int @@ -3283,7 +3282,8 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); if (status) goto out; - if (s->sc_type == NFS4_DELEG_STID) { + switch (s->sc_type) { + case NFS4_DELEG_STID: dp = delegstateid(s); status = nfs4_check_delegmode(dp, flags); if (status) @@ -3293,10 +3293,12 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, *filpp = dp->dl_file->fi_deleg_file; BUG_ON(!*filpp); } - } else { /* open or lock stateid */ + break; + case NFS4_OPEN_STID: + case NFS4_LOCK_STID: stp = openlockstateid(s); - status = nfserr_bad_stateid; - if (nfs4_check_fh(current_fh, stp)) + status = nfs4_check_fh(current_fh, stp); + if (status) goto out; if (stp->st_stateowner->so_is_open_owner && !(openowner(stp->st_stateowner)->oo_flags & NFS4_OO_CONFIRMED)) @@ -3311,6 +3313,9 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, else *filpp = find_writeable_file(stp->st_file); } + break; + default: + return nfserr_bad_stateid; } status = nfs_ok; out: @@ -3362,6 +3367,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ret = nfsd4_free_lock_stateid(openlockstateid(s)); else ret = nfserr_locks_held; + break; + default: + ret = nfserr_bad_stateid; } out: nfs4_unlock_state(); @@ -3390,12 +3398,19 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_ struct nfs4_stateowner *sop = stp->st_stateowner; __be32 status; - if (nfs4_check_fh(current_fh, stp)) - return nfserr_bad_stateid; status = nfsd4_check_seqid(cstate, sop, seqid); if (status) return status; - return check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); + if (stp->st_stid.sc_type == NFS4_CLOSED_STID) + /* + * "Closed" stateid's exist *only* to return + * nfserr_replay_me from the previous step. + */ + return nfserr_bad_stateid; + status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); + if (status) + return status; + return nfs4_check_fh(current_fh, stp); } /* @@ -3564,8 +3579,13 @@ void nfsd4_purge_closed_stateid(struct nfs4_stateowner *so) return; } oo->oo_flags &= ~NFS4_OO_PURGE_CLOSE; - free_generic_stateid(oo->oo_last_closed_stid); - oo->oo_last_closed_stid = NULL; + release_last_closed_stateid(oo); +} + +static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) +{ + unhash_open_stateid(s); + s->st_stid.sc_type = NFS4_CLOSED_STID; } /* @@ -3584,24 +3604,10 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, cstate->current_fh.fh_dentry->d_name.name); nfs4_lock_state(); - /* check close_lru for replay */ - status = nfs4_preprocess_confirmed_seqid_op(cstate, close->cl_seqid, - &close->cl_stateid, &stp); - if (stp == NULL && status == nfserr_expired) { - /* - * Also, we should make sure this isn't just the result of - * a replayed close: - */ - oo = search_close_lru(&close->cl_stateid); - /* It's not stale; let's assume it's expired: */ - if (oo == NULL) - goto out; - cstate->replay_owner = &oo->oo_owner; - status = nfsd4_check_seqid(cstate, &oo->oo_owner, close->cl_seqid); - if (status) - goto out; - status = nfserr_bad_seqid; - } + status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid, + &close->cl_stateid, + NFS4_OPEN_STID|NFS4_CLOSED_STID, + &stp); if (status) goto out; oo = openowner(stp->st_stateowner); @@ -3609,9 +3615,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, update_stateid(&stp->st_stid.sc_stateid); memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); - /* unhash_open_stateid() calls nfsd_close() if needed */ + nfsd4_close_open_stateid(stp); oo->oo_last_closed_stid = stp; - unhash_open_stateid(stp); /* place unused nfs4_stateowners on so_close_lru list to be * released by the laundromat service after the lease period diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index d6aec4f8d3dd..da68bf66a3d7 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -76,7 +76,9 @@ struct nfs4_stid { #define NFS4_OPEN_STID 1 #define NFS4_LOCK_STID 2 #define NFS4_DELEG_STID 4 - char sc_type; +/* For an open stateid kept around *only* to process close replays: */ +#define NFS4_CLOSED_STID 8 + unsigned char sc_type; struct list_head sc_hash; stateid_t sc_stateid; }; From 3d02fa29dec920c597dd7b7db608a4bc71f088ce Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 19 Sep 2011 15:07:41 -0400 Subject: [PATCH 072/103] nfsd4: fix open downgrade, again Yet another open-management regression: - nfs4_file_downgrade() doesn't remove the BOTH access bit on downgrade, so the server's idea of the stateid's access gets out of sync with the client's. If we want to keep an O_RDWR open in this case, we should do that in the file_put_access logic rather than here. - We forgot to convert v4 access to an open mode here. This logic has proven too hard to get right. In the future we may consider: - reexamining the lock/openowner relationship (locks probably don't really need to take their own references here). - adding open upgrade/downgrade support to the vfs. - removing the atomic operations. They're redundant as long as this is all under some other lock. Also, maybe some kind of additional static checking would help catch O_/NFS4_SHARE_ACCESS confusion. Cc: stable@kernel.org Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e5cba833613f..edcced18caa8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -194,8 +194,15 @@ static void nfs4_file_put_fd(struct nfs4_file *fp, int oflag) static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag) { if (atomic_dec_and_test(&fp->fi_access[oflag])) { - nfs4_file_put_fd(fp, O_RDWR); nfs4_file_put_fd(fp, oflag); + /* + * It's also safe to get rid of the RDWR open *if* + * we no longer have need of the other kind of access + * or if we already have the other kind of open: + */ + if (fp->fi_fds[1-oflag] + || atomic_read(&fp->fi_access[1 - oflag]) == 0) + nfs4_file_put_fd(fp, O_RDWR); } } @@ -3500,8 +3507,9 @@ static inline void nfs4_file_downgrade(struct nfs4_ol_stateid *stp, unsigned int int i; for (i = 1; i < 4; i++) { - if (test_bit(i, &stp->st_access_bmap) && !(i & to_access)) { - nfs4_file_put_access(stp->st_file, i); + if (test_bit(i, &stp->st_access_bmap) + && ((i & to_access) != i)) { + nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(i)); __clear_bit(i, &stp->st_access_bmap); } } From c856694e3d46976c76bf5b92091cb1efa211208d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 20 Sep 2011 08:49:51 -0400 Subject: [PATCH 073/103] nfsd4: make op_cacheresult another flag I'm not sure why I used a new field for this originally. Also, the differences between some of these flags are a little subtle; add some comments to explain. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 50 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index aa769dfa756a..4e41f65c7021 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1013,14 +1013,15 @@ enum nfsd4_op_flags { /* For rfc 5661 section 2.6.3.1.1: */ OP_HANDLES_WRONGSEC = 1 << 3, OP_IS_PUTFH_LIKE = 1 << 4, - OP_MODIFIES_SOMETHING = 1 << 5, /* op is non-idempotent */ -}; - -struct nfsd4_operation { - nfsd4op_func op_func; - u32 op_flags; - char *op_name; /* + * These are the ops whose result size we estimate before + * encoding, to avoid performing an op then not being able to + * respond or cache a response. This includes writes and setattrs + * as well as the operations usually called "nonidempotent": + */ + OP_MODIFIES_SOMETHING = 1 << 5, + /* + * Cache compounds containing these ops in the xid-based drc: * We use the DRC for compounds containing non-idempotent * operations, *except* those that are 4.1-specific (since * sessions provide their own EOS), and except for stateful @@ -1028,7 +1029,13 @@ struct nfsd4_operation { * (since sequence numbers provide EOS for open, lock, etc in * the v4.0 case). */ - bool op_cacheresult; + OP_CACHEME = 1 << 6, +}; + +struct nfsd4_operation { + nfsd4op_func op_func; + u32 op_flags; + char *op_name; /* Try to get response size before operation */ nfsd4op_rsize op_rsize_bop; }; @@ -1077,7 +1084,7 @@ static inline struct nfsd4_operation *OPDESC(struct nfsd4_op *op) bool nfsd4_cache_this_op(struct nfsd4_op *op) { - return OPDESC(op)->op_cacheresult; + return OPDESC(op)->op_flags & OP_CACHEME; } static bool need_wrongsec_check(struct svc_rqst *rqstp) @@ -1420,9 +1427,8 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_CREATE] = { .op_func = (nfsd4op_func)nfsd4_create, - .op_flags = OP_MODIFIES_SOMETHING, + .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, .op_name = "OP_CREATE", - .op_cacheresult = true, .op_rsize_bop = (nfsd4op_rsize)nfsd4_create_rsize, }, [OP_DELEGRETURN] = { @@ -1442,9 +1448,9 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_LINK] = { .op_func = (nfsd4op_func)nfsd4_link, - .op_flags = ALLOWED_ON_ABSENT_FS | OP_MODIFIES_SOMETHING, + .op_flags = ALLOWED_ON_ABSENT_FS | OP_MODIFIES_SOMETHING + | OP_CACHEME, .op_name = "OP_LINK", - .op_cacheresult = true, .op_rsize_bop = (nfsd4op_rsize)nfsd4_link_rsize, }, [OP_LOCK] = { @@ -1534,16 +1540,14 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_REMOVE] = { .op_func = (nfsd4op_func)nfsd4_remove, - .op_flags = OP_MODIFIES_SOMETHING, + .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, .op_name = "OP_REMOVE", - .op_cacheresult = true, .op_rsize_bop = (nfsd4op_rsize)nfsd4_remove_rsize, }, [OP_RENAME] = { .op_func = (nfsd4op_func)nfsd4_rename, - .op_flags = OP_MODIFIES_SOMETHING, + .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, .op_name = "OP_RENAME", - .op_cacheresult = true, .op_rsize_bop = (nfsd4op_rsize)nfsd4_rename_rsize, }, [OP_RENEW] = { @@ -1575,24 +1579,21 @@ static struct nfsd4_operation nfsd4_ops[] = { [OP_SETATTR] = { .op_func = (nfsd4op_func)nfsd4_setattr, .op_name = "OP_SETATTR", - .op_flags = OP_MODIFIES_SOMETHING, - .op_cacheresult = true, + .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, .op_rsize_bop = (nfsd4op_rsize)nfsd4_setattr_rsize, }, [OP_SETCLIENTID] = { .op_func = (nfsd4op_func)nfsd4_setclientid, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS - | OP_MODIFIES_SOMETHING, + | OP_MODIFIES_SOMETHING | OP_CACHEME, .op_name = "OP_SETCLIENTID", - .op_cacheresult = true, .op_rsize_bop = (nfsd4op_rsize)nfsd4_setclientid_rsize, }, [OP_SETCLIENTID_CONFIRM] = { .op_func = (nfsd4op_func)nfsd4_setclientid_confirm, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS - | OP_MODIFIES_SOMETHING, + | OP_MODIFIES_SOMETHING | OP_CACHEME, .op_name = "OP_SETCLIENTID_CONFIRM", - .op_cacheresult = true, .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, [OP_VERIFY] = { @@ -1601,9 +1602,8 @@ static struct nfsd4_operation nfsd4_ops[] = { }, [OP_WRITE] = { .op_func = (nfsd4op_func)nfsd4_write, - .op_flags = OP_MODIFIES_SOMETHING, + .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, .op_name = "OP_WRITE", - .op_cacheresult = true, .op_rsize_bop = (nfsd4op_rsize)nfsd4_write_rsize, }, [OP_RELEASE_LOCKOWNER] = { From 8335ebd94b3f5bed7875cc35848bbe46d8381695 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 21 Sep 2011 08:34:32 -0400 Subject: [PATCH 074/103] leases: split up generic_setlease into lock/unlock cases Eventually we should probably do the same thing to the file operations as well. Signed-off-by: J. Bruce Fields --- fs/locks.c | 98 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 9b8408eb6946..b342902c38bc 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1352,18 +1352,7 @@ int fcntl_getlease(struct file *filp) return type; } -/** - * generic_setlease - sets a lease on an open file - * @filp: file pointer - * @arg: type of lease to obtain - * @flp: input - file_lock to use, output - file_lock inserted - * - * The (input) flp->fl_lmops->lm_break function is required - * by break_lease(). - * - * Called with file_lock_lock held. - */ -int generic_setlease(struct file *filp, long arg, struct file_lock **flp) +int generic_add_lease(struct file *filp, long arg, struct file_lock **flp) { struct file_lock *fl, **before, **my_before = NULL, *lease; struct dentry *dentry = filp->f_path.dentry; @@ -1372,29 +1361,13 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) lease = *flp; - error = -EACCES; - if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE)) + error = -EAGAIN; + if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) goto out; - error = -EINVAL; - if (!S_ISREG(inode->i_mode)) + if ((arg == F_WRLCK) + && ((dentry->d_count > 1) + || (atomic_read(&inode->i_count) > 1))) goto out; - error = security_file_lock(filp, arg); - if (error) - goto out; - - time_out_leases(inode); - - BUG_ON(!(*flp)->fl_lmops->lm_break); - - if (arg != F_UNLCK) { - error = -EAGAIN; - if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) - goto out; - if ((arg == F_WRLCK) - && ((dentry->d_count > 1) - || (atomic_read(&inode->i_count) > 1))) - goto out; - } /* * At this point, we know that if there is an exclusive @@ -1433,9 +1406,6 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) goto out; } - if (arg == F_UNLCK) - goto out; - error = -EINVAL; if (!leases_enable) goto out; @@ -1446,6 +1416,62 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) out: return error; } + +int generic_delete_lease(struct file *filp, struct file_lock **flp) +{ + struct file_lock *fl, **before; + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = dentry->d_inode; + + for (before = &inode->i_flock; + ((fl = *before) != NULL) && IS_LEASE(fl); + before = &fl->fl_next) { + if (fl->fl_file != filp) + continue; + return (*flp)->fl_lmops->lm_change(before, F_UNLCK); + } + return -EAGAIN; +} + +/** + * generic_setlease - sets a lease on an open file + * @filp: file pointer + * @arg: type of lease to obtain + * @flp: input - file_lock to use, output - file_lock inserted + * + * The (input) flp->fl_lmops->lm_break function is required + * by break_lease(). + * + * Called with file_lock_lock held. + */ +int generic_setlease(struct file *filp, long arg, struct file_lock **flp) +{ + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = dentry->d_inode; + int error; + + if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE)) + return -EACCES; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + error = security_file_lock(filp, arg); + if (error) + return error; + + time_out_leases(inode); + + BUG_ON(!(*flp)->fl_lmops->lm_break); + + switch (arg) { + case F_UNLCK: + return generic_delete_lease(filp, flp); + case F_RDLCK: + case F_WRLCK: + return generic_add_lease(filp, arg, flp); + default: + BUG(); + } +} EXPORT_SYMBOL(generic_setlease); static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease) From 2a74aba799bfbc02977b69400b7bf4d2850aea79 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 23 Sep 2011 17:20:02 -0400 Subject: [PATCH 075/103] nfsd4: move client * to nfs4_stateid, add init_stid helper This will be convenient. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 8 +++---- fs/nfsd/nfs4state.c | 48 +++++++++++++++++++++++------------------- fs/nfsd/state.h | 2 +- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 93b5e405ad38..de018ecadae6 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -787,7 +787,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) { struct nfsd4_callback *cb = calldata; struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); - struct nfs4_client *clp = dp->dl_client; + struct nfs4_client *clp = dp->dl_stid.sc_client; u32 minorversion = clp->cl_minorversion; cb->cb_minorversion = minorversion; @@ -809,7 +809,7 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) { struct nfsd4_callback *cb = calldata; struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); - struct nfs4_client *clp = dp->dl_client; + struct nfs4_client *clp = dp->dl_stid.sc_client; dprintk("%s: minorversion=%d\n", __func__, clp->cl_minorversion); @@ -832,7 +832,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) { struct nfsd4_callback *cb = calldata; struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); - struct nfs4_client *clp = dp->dl_client; + struct nfs4_client *clp = dp->dl_stid.sc_client; struct rpc_clnt *current_rpc_client = clp->cl_cb_client; nfsd4_cb_done(task, calldata); @@ -1006,7 +1006,7 @@ void nfsd4_do_callback_rpc(struct work_struct *w) void nfsd4_cb_recall(struct nfs4_delegation *dp) { struct nfsd4_callback *cb = &dp->dl_recall; - struct nfs4_client *clp = dp->dl_client; + struct nfs4_client *clp = dp->dl_stid.sc_client; dp->dl_retries = 1; cb->cb_op = dp; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index edcced18caa8..cb36c9a2e8ca 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -224,6 +224,19 @@ static inline void hash_stid(struct nfs4_stid *stid) list_add(&stid->sc_hash, &stateid_hashtbl[hashval]); } +static void init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, unsigned char type) +{ + stateid_t *s = &stid->sc_stateid; + + stid->sc_type = type; + stid->sc_client = cl; + s->si_opaque.so_clid = cl->cl_clientid; + s->si_opaque.so_id = current_stateid++; + /* Will be incremented before return to client: */ + s->si_generation = 0; + hash_stid(stid); +} + static struct nfs4_delegation * alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh, u32 type) { @@ -245,19 +258,20 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); if (dp == NULL) return dp; + init_stid(&dp->dl_stid, clp, NFS4_DELEG_STID); + /* + * delegation seqid's are never incremented. The 4.1 special + * meaning of seqid 0 isn't really meaningful, really, but let's + * avoid 0 anyway just for consistency and use 1: + */ + dp->dl_stid.sc_stateid.si_generation = 1; num_delegations++; INIT_LIST_HEAD(&dp->dl_perfile); INIT_LIST_HEAD(&dp->dl_perclnt); INIT_LIST_HEAD(&dp->dl_recall_lru); - dp->dl_client = clp; get_nfs4_file(fp); dp->dl_file = fp; dp->dl_type = type; - dp->dl_stid.sc_type = NFS4_DELEG_STID; - dp->dl_stid.sc_stateid.si_opaque.so_clid = clp->cl_clientid; - dp->dl_stid.sc_stateid.si_opaque.so_id = current_stateid++; - dp->dl_stid.sc_stateid.si_generation = 1; - hash_stid(&dp->dl_stid); fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); @@ -2333,18 +2347,13 @@ init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd struct nfs4_openowner *oo = open->op_openowner; struct nfs4_client *clp = oo->oo_owner.so_client; + init_stid(&stp->st_stid, clp, NFS4_OPEN_STID); INIT_LIST_HEAD(&stp->st_lockowners); list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); - stp->st_stid.sc_type = NFS4_OPEN_STID; stp->st_stateowner = &oo->oo_owner; get_nfs4_file(fp); stp->st_file = fp; - stp->st_stid.sc_stateid.si_opaque.so_clid = clp->cl_clientid; - stp->st_stid.sc_stateid.si_opaque.so_id = current_stateid++; - /* note will be incremented before first return to client: */ - stp->st_stid.sc_stateid.si_generation = 0; - hash_stid(&stp->st_stid); stp->st_access_bmap = 0; stp->st_deny_bmap = 0; __set_bit(open->op_share_access & ~NFS4_SHARE_WANT_MASK, @@ -2792,7 +2801,7 @@ static int nfs4_setlease(struct nfs4_delegation *dp, int flag) if (!fl) return -ENOMEM; fl->fl_file = find_readable_file(fp); - list_add(&dp->dl_perclnt, &dp->dl_client->cl_delegations); + list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations); status = vfs_setlease(fl->fl_file, fl->fl_type, &fl); if (status) { list_del_init(&dp->dl_perclnt); @@ -2821,7 +2830,7 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag) atomic_inc(&fp->fi_delegees); list_add(&dp->dl_perfile, &fp->fi_delegations); spin_unlock(&recall_lock); - list_add(&dp->dl_perclnt, &dp->dl_client->cl_delegations); + list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations); return 0; } @@ -3295,7 +3304,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, status = nfs4_check_delegmode(dp, flags); if (status) goto out; - renew_client(dp->dl_client); + renew_client(dp->dl_stid.sc_client); if (filpp) { *filpp = dp->dl_file->fi_deleg_file; BUG_ON(!*filpp); @@ -3665,7 +3674,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate)); if (status) goto out; - renew_client(dp->dl_client); + renew_client(dp->dl_stid.sc_client); unhash_delegation(dp); out: @@ -3819,17 +3828,12 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct stp = nfs4_alloc_stateid(); if (stp == NULL) goto out; + init_stid(&stp->st_stid, clp, NFS4_LOCK_STID); list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); stp->st_stateowner = &lo->lo_owner; - stp->st_stid.sc_type = NFS4_LOCK_STID; get_nfs4_file(fp); stp->st_file = fp; - stp->st_stid.sc_stateid.si_opaque.so_clid = clp->cl_clientid; - stp->st_stid.sc_stateid.si_opaque.so_id = current_stateid++; - /* note will be incremented before first return to client: */ - stp->st_stid.sc_stateid.si_generation = 0; - hash_stid(&stp->st_stid); stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_openstp = open_stp; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index da68bf66a3d7..70062b75e24a 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -81,6 +81,7 @@ struct nfs4_stid { unsigned char sc_type; struct list_head sc_hash; stateid_t sc_stateid; + struct nfs4_client *sc_client; }; struct nfs4_delegation { @@ -88,7 +89,6 @@ struct nfs4_delegation { struct list_head dl_perclnt; struct list_head dl_recall_lru; /* delegation recalled */ atomic_t dl_count; /* ref count */ - struct nfs4_client *dl_client; struct nfs4_file *dl_file; u32 dl_type; time_t dl_time; From 6136d2b409652b064b2da6d43d5c47cbd1d2cc14 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 23 Sep 2011 16:21:15 -0400 Subject: [PATCH 076/103] nfsd4: use idr for stateid's The idr system is designed exactly for generating id and looking up integer id's. Thanks to Trond for pointing it out. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 124 ++++++++++++++++++++++++++------------------ fs/nfsd/state.h | 1 - 2 files changed, 73 insertions(+), 52 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cb36c9a2e8ca..a9e71cdf4a84 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -32,6 +32,7 @@ * */ +#include #include #include #include @@ -49,7 +50,6 @@ time_t nfsd4_lease = 90; /* default lease time */ time_t nfsd4_grace = 90; static time_t boot_time; -static u32 current_stateid = 1; static stateid_t zerostateid; /* bits all 0 */ static stateid_t onestateid; /* bits all 1 */ static u64 current_sessionid = 1; @@ -149,10 +149,7 @@ static struct list_head open_ownerstr_hashtbl[OPEN_OWNER_HASH_SIZE]; #define FILE_HASH_BITS 8 #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) -/* hash table for (open)nfs4_ol_stateid */ -#define STATEID_HASH_BITS 10 -#define STATEID_HASH_SIZE (1 << STATEID_HASH_BITS) -#define STATEID_HASH_MASK (STATEID_HASH_SIZE - 1) +struct idr stateids; static unsigned int file_hashval(struct inode *ino) { @@ -160,13 +157,7 @@ static unsigned int file_hashval(struct inode *ino) return hash_ptr(ino, FILE_HASH_BITS); } -static unsigned int stateid_hashval(stateid_t *s) -{ - return opaque_hashval(&s->si_opaque, sizeof(stateid_opaque_t)) & STATEID_HASH_MASK; -} - static struct list_head file_hashtbl[FILE_HASH_SIZE]; -static struct list_head stateid_hashtbl[STATEID_HASH_SIZE]; static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag) { @@ -215,26 +206,52 @@ static void nfs4_file_put_access(struct nfs4_file *fp, int oflag) __nfs4_file_put_access(fp, oflag); } -static inline void hash_stid(struct nfs4_stid *stid) +static inline int get_new_stid(struct nfs4_stid *stid) { - stateid_t *s = &stid->sc_stateid; - unsigned int hashval; + static int min_stateid = 0; + int new_stid; + int error; - hashval = stateid_hashval(s); - list_add(&stid->sc_hash, &stateid_hashtbl[hashval]); + if (!idr_pre_get(&stateids, GFP_KERNEL)) + return -ENOMEM; + + error = idr_get_new_above(&stateids, stid, min_stateid, &new_stid); + /* + * All this code is currently serialized; the preallocation + * above should still be ours: + */ + BUG_ON(error); + /* + * It shouldn't be a problem to reuse an opaque stateid value. + * I don't think it is for 4.1. But with 4.0 I worry that, for + * example, a stray write retransmission could be accepted by + * the server when it should have been rejected. Therefore, + * adopt a trick from the sctp code to attempt to maximize the + * amount of time until an id is reused, by ensuring they always + * "increase" (mod INT_MAX): + */ + + min_stateid = new_stid+1; + if (min_stateid == INT_MAX) + min_stateid = 0; + return new_stid; } -static void init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, unsigned char type) +static inline __be32 init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, unsigned char type) { stateid_t *s = &stid->sc_stateid; + int new_id; stid->sc_type = type; stid->sc_client = cl; s->si_opaque.so_clid = cl->cl_clientid; - s->si_opaque.so_id = current_stateid++; + new_id = get_new_stid(stid); + if (new_id < 0) + return nfserr_jukebox; + s->si_opaque.so_id = (u32)new_id; /* Will be incremented before return to client: */ s->si_generation = 0; - hash_stid(stid); + return 0; } static struct nfs4_delegation * @@ -242,6 +259,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv { struct nfs4_delegation *dp; struct nfs4_file *fp = stp->st_file; + __be32 status; dprintk("NFSD alloc_init_deleg\n"); /* @@ -258,11 +276,15 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); if (dp == NULL) return dp; - init_stid(&dp->dl_stid, clp, NFS4_DELEG_STID); + status = init_stid(&dp->dl_stid, clp, NFS4_DELEG_STID); + if (status) { + kmem_cache_free(deleg_slab, dp); + return NULL; + } /* * delegation seqid's are never incremented. The 4.1 special - * meaning of seqid 0 isn't really meaningful, really, but let's - * avoid 0 anyway just for consistency and use 1: + * meaning of seqid 0 isn't meaningful, really, but let's avoid + * 0 anyway just for consistency and use 1: */ dp->dl_stid.sc_stateid.si_generation = 1; num_delegations++; @@ -300,11 +322,16 @@ static void nfs4_put_deleg_lease(struct nfs4_file *fp) } } +static void unhash_stid(struct nfs4_stid *s) +{ + idr_remove(&stateids, s->sc_stateid.si_opaque.so_id); +} + /* Called under the state lock. */ static void unhash_delegation(struct nfs4_delegation *dp) { - list_del_init(&dp->dl_stid.sc_hash); + unhash_stid(&dp->dl_stid); list_del_init(&dp->dl_perclnt); spin_lock(&recall_lock); list_del_init(&dp->dl_perfile); @@ -457,7 +484,7 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp) struct file *file; unhash_generic_stateid(stp); - list_del(&stp->st_stid.sc_hash); + unhash_stid(&stp->st_stid); file = find_any_file(stp->st_file); if (file) locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner)); @@ -506,7 +533,7 @@ static void unhash_open_stateid(struct nfs4_ol_stateid *stp) static void release_open_stateid(struct nfs4_ol_stateid *stp) { unhash_open_stateid(stp); - list_del(&stp->st_stid.sc_hash); + unhash_stid(&stp->st_stid); free_generic_stateid(stp); } @@ -528,7 +555,7 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo) struct nfs4_ol_stateid *s = oo->oo_last_closed_stid; if (s) { - list_del_init(&s->st_stid.sc_hash); + unhash_stid(&s->st_stid); free_generic_stateid(s); oo->oo_last_closed_stid = NULL; } @@ -1099,23 +1126,9 @@ static void gen_confirm(struct nfs4_client *clp) *p++ = i++; } -static int -same_stateid(stateid_t *id_one, stateid_t *id_two) -{ - return 0 == memcmp(&id_one->si_opaque, &id_two->si_opaque, - sizeof(stateid_opaque_t)); -} - static struct nfs4_stid *find_stateid(stateid_t *t) { - struct nfs4_stid *s; - unsigned int hashval; - - hashval = stateid_hashval(t); - list_for_each_entry(s, &stateid_hashtbl[hashval], sc_hash) - if (same_stateid(&s->sc_stateid, t)) - return s; - return NULL; + return idr_find(&stateids, t->si_opaque.so_id); } static struct nfs4_stid *find_stateid_by_type(stateid_t *t, char typemask) @@ -2342,12 +2355,14 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str return oo; } -static inline void -init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { +static inline __be32 init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { struct nfs4_openowner *oo = open->op_openowner; struct nfs4_client *clp = oo->oo_owner.so_client; + __be32 status; - init_stid(&stp->st_stid, clp, NFS4_OPEN_STID); + status = init_stid(&stp->st_stid, clp, NFS4_OPEN_STID); + if (status) + return status; INIT_LIST_HEAD(&stp->st_lockowners); list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); @@ -2360,6 +2375,7 @@ init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd &stp->st_access_bmap); __set_bit(open->op_share_deny, &stp->st_deny_bmap); stp->st_openstp = NULL; + return nfs_ok; } static void @@ -2949,7 +2965,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status = nfs4_new_open(rqstp, &stp, fp, current_fh, open); if (status) goto out; - init_open_stateid(stp, fp, open); + status = init_open_stateid(stp, fp, open); + if (status) { + release_open_stateid(stp); + goto out; + } status = nfsd4_truncate(rqstp, current_fh, open); if (status) { release_open_stateid(stp); @@ -3824,11 +3844,16 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct { struct nfs4_ol_stateid *stp; struct nfs4_client *clp = lo->lo_owner.so_client; + __be32 status; stp = nfs4_alloc_stateid(); if (stp == NULL) - goto out; - init_stid(&stp->st_stid, clp, NFS4_LOCK_STID); + return NULL; + status = init_stid(&stp->st_stid, clp, NFS4_LOCK_STID); + if (status) { + free_generic_stateid(stp); + return NULL; + } list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); stp->st_stateowner = &lo->lo_owner; @@ -3837,8 +3862,6 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_openstp = open_stp; - -out: return stp; } @@ -4386,8 +4409,7 @@ nfs4_state_init(void) for (i = 0; i < OPEN_OWNER_HASH_SIZE; i++) { INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]); } - for (i = 0; i < STATEID_HASH_SIZE; i++) - INIT_LIST_HEAD(&stateid_hashtbl[i]); + idr_init(&stateids); for (i = 0; i < LOCK_HASH_SIZE; i++) { INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); } diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 70062b75e24a..3ed5f99141ec 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -79,7 +79,6 @@ struct nfs4_stid { /* For an open stateid kept around *only* to process close replays: */ #define NFS4_CLOSED_STID 8 unsigned char sc_type; - struct list_head sc_hash; stateid_t sc_stateid; struct nfs4_client *sc_client; }; From 36279ac10c3d69372af875f1affafd375db687a9 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 26 Sep 2011 12:53:00 -0400 Subject: [PATCH 077/103] nfsd4: assume test_stateid always has session Test_stateid is 4.1-only and only allowed after a sequence operation, so this check is unnecessary. Cc: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 6 +++--- fs/nfsd/nfs4xdr.c | 2 +- fs/nfsd/state.h | 2 +- fs/nfsd/xdr4.h | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a9e71cdf4a84..daf75fa4c027 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3256,7 +3256,7 @@ static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_sess return nfserr_old_stateid; } -__be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) +__be32 nfs4_validate_stateid(stateid_t *stateid) { struct nfs4_stid *s; struct nfs4_ol_stateid *ols; @@ -3268,7 +3268,7 @@ __be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session) s = find_stateid(stateid); if (!s) return nfserr_stale_stateid; - status = check_stateid_generation(stateid, &s->sc_stateid, has_session); + status = check_stateid_generation(stateid, &s->sc_stateid, 1); if (status) return status; if (!(s->sc_type & (NFS4_OPEN_STID | NFS4_LOCK_STID))) @@ -3374,7 +3374,7 @@ __be32 nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_test_stateid *test_stateid) { - test_stateid->ts_has_session = nfsd4_has_session(cstate); + /* real work is done during encoding */ return nfs_ok; } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 7bd57c2dbc4d..2429fffa31dd 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3302,7 +3302,7 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr, nfs4_lock_state(); for (i = 0; i < test_stateid->ts_num_ids; i++) { nfsd4_decode_stateid(argp, &si); - valid = nfs4_validate_stateid(&si, test_stateid->ts_has_session); + valid = nfs4_validate_stateid(&si); RESERVE_SPACE(4); *p++ = htonl(valid); resp->p = p; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 3ed5f99141ec..55a4d6a108a2 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -508,7 +508,7 @@ extern void nfsd4_recdir_purge_old(void); extern int nfsd4_create_clid_dir(struct nfs4_client *clp); extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); extern void release_session_client(struct nfsd4_session *); -extern __be32 nfs4_validate_stateid(stateid_t *, bool); +extern __be32 nfs4_validate_stateid(stateid_t *); extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); #endif /* NFSD4_STATE_H */ diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index a767b57b8208..c9012149637c 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -343,7 +343,6 @@ struct nfsd4_saved_compoundargs { struct nfsd4_test_stateid { __be32 ts_num_ids; - bool ts_has_session; struct nfsd4_compoundargs *ts_saved_args; struct nfsd4_saved_compoundargs ts_savedp; }; From 38c2f4b12a455cb3a108fd5c79a10df2ba3ec9a7 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 23 Sep 2011 17:01:19 -0400 Subject: [PATCH 078/103] nfsd4: look up stateid's per clientid Use a separate stateid idr per client, and lookup a stateid by first finding the client, then looking up the stateid relative to that client. Also some minor refactoring. This allows us to improve error returns: we can return expired when the clientid is not found and bad_stateid when the clientid is found but not the stateid, as opposed to returning expired for both cases. I hope this will also help to replace the state lock mostly by a per-client lock, but that hasn't been done yet. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 112 +++++++++++++++++++------------------------- fs/nfsd/nfs4xdr.c | 3 +- fs/nfsd/state.h | 4 +- 3 files changed, 54 insertions(+), 65 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index daf75fa4c027..931155f51ecc 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -32,7 +32,6 @@ * */ -#include #include #include #include @@ -149,8 +148,6 @@ static struct list_head open_ownerstr_hashtbl[OPEN_OWNER_HASH_SIZE]; #define FILE_HASH_BITS 8 #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) -struct idr stateids; - static unsigned int file_hashval(struct inode *ino) { /* XXX: why are we hashing on inode pointer, anyway? */ @@ -209,13 +206,14 @@ static void nfs4_file_put_access(struct nfs4_file *fp, int oflag) static inline int get_new_stid(struct nfs4_stid *stid) { static int min_stateid = 0; + struct idr *stateids = &stid->sc_client->cl_stateids; int new_stid; int error; - if (!idr_pre_get(&stateids, GFP_KERNEL)) + if (!idr_pre_get(stateids, GFP_KERNEL)) return -ENOMEM; - error = idr_get_new_above(&stateids, stid, min_stateid, &new_stid); + error = idr_get_new_above(stateids, stid, min_stateid, &new_stid); /* * All this code is currently serialized; the preallocation * above should still be ours: @@ -324,7 +322,9 @@ static void nfs4_put_deleg_lease(struct nfs4_file *fp) static void unhash_stid(struct nfs4_stid *s) { - idr_remove(&stateids, s->sc_stateid.si_opaque.so_id); + struct idr *stateids = &s->sc_client->cl_stateids; + + idr_remove(stateids, s->sc_stateid.si_opaque.so_id); } /* Called under the state lock. */ @@ -1126,16 +1126,16 @@ static void gen_confirm(struct nfs4_client *clp) *p++ = i++; } -static struct nfs4_stid *find_stateid(stateid_t *t) +static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t) { - return idr_find(&stateids, t->si_opaque.so_id); + return idr_find(&cl->cl_stateids, t->si_opaque.so_id); } -static struct nfs4_stid *find_stateid_by_type(stateid_t *t, char typemask) +static struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl, stateid_t *t, char typemask) { struct nfs4_stid *s; - s = find_stateid(t); + s = find_stateid(cl, t); if (!s) return NULL; if (typemask & s->sc_type) @@ -1143,16 +1143,6 @@ static struct nfs4_stid *find_stateid_by_type(stateid_t *t, char typemask) return NULL; } -static struct nfs4_ol_stateid *find_ol_stateid_by_type(stateid_t *t, char typemask) -{ - struct nfs4_stid *s; - - s = find_stateid_by_type(t, typemask); - if (!s) - return NULL; - return openlockstateid(s); -} - static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, struct svc_rqst *rqstp, nfs4_verifier *verf) { @@ -1175,6 +1165,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, } } + idr_init(&clp->cl_stateids); memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); atomic_set(&clp->cl_refcount, 0); clp->cl_cb_state = NFSD4_CB_UNKNOWN; @@ -2611,24 +2602,24 @@ static int share_access_to_flags(u32 share_access) return share_access == NFS4_SHARE_ACCESS_READ ? RD_STATE : WR_STATE; } -static struct nfs4_delegation *find_deleg_stateid(stateid_t *s) +static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, stateid_t *s) { struct nfs4_stid *ret; - ret = find_stateid_by_type(s, NFS4_DELEG_STID); + ret = find_stateid_by_type(cl, s, NFS4_DELEG_STID); if (!ret) return NULL; return delegstateid(ret); } static __be32 -nfs4_check_deleg(struct nfs4_file *fp, struct nfsd4_open *open, +nfs4_check_deleg(struct nfs4_client *cl, struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_delegation **dp) { int flags; __be32 status = nfserr_bad_stateid; - *dp = find_deleg_stateid(&open->op_delegate_stateid); + *dp = find_deleg_stateid(cl, &open->op_delegate_stateid); if (*dp == NULL) goto out; flags = share_access_to_flags(open->op_share_access); @@ -2920,6 +2911,7 @@ __be32 nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) { struct nfsd4_compoundres *resp = rqstp->rq_resp; + struct nfs4_client *cl = open->op_openowner->oo_owner.so_client; struct nfs4_file *fp = NULL; struct inode *ino = current_fh->fh_dentry->d_inode; struct nfs4_ol_stateid *stp = NULL; @@ -2939,7 +2931,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf if (fp) { if ((status = nfs4_check_open(fp, open, &stp))) goto out; - status = nfs4_check_deleg(fp, open, &dp); + status = nfs4_check_deleg(cl, fp, open, &dp); if (status) goto out; } else { @@ -3256,7 +3248,7 @@ static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_sess return nfserr_old_stateid; } -__be32 nfs4_validate_stateid(stateid_t *stateid) +__be32 nfs4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) { struct nfs4_stid *s; struct nfs4_ol_stateid *ols; @@ -3265,7 +3257,7 @@ __be32 nfs4_validate_stateid(stateid_t *stateid) if (STALE_STATEID(stateid)) return nfserr_stale_stateid; - s = find_stateid(stateid); + s = find_stateid(cl, stateid); if (!s) return nfserr_stale_stateid; status = check_stateid_generation(stateid, &s->sc_stateid, 1); @@ -3280,6 +3272,24 @@ __be32 nfs4_validate_stateid(stateid_t *stateid) return nfs_ok; } +static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s) +{ + struct nfs4_client *cl; + + if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) + return nfserr_bad_stateid; + if (STALE_STATEID(stateid)) + return nfserr_stale_stateid; + cl = find_confirmed_client(&stateid->si_opaque.so_clid); + if (!cl) + return nfserr_expired; + *s = find_stateid_by_type(cl, stateid, typemask); + if (!*s) + return nfserr_bad_stateid; + return nfs_ok; + +} + /* * Checks for stateid operations */ @@ -3303,18 +3313,9 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) return check_special_stateids(current_fh, stateid, flags); - status = nfserr_stale_stateid; - if (STALE_STATEID(stateid)) - goto out; - - /* - * We assume that any stateid that has the current boot time, - * but that we can't find, is expired: - */ - status = nfserr_expired; - s = find_stateid(stateid); - if (!s) - goto out; + status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s); + if (status) + return status; status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); if (status) goto out; @@ -3384,10 +3385,11 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { stateid_t *stateid = &free_stateid->fr_stateid; struct nfs4_stid *s; + struct nfs4_client *cl = cstate->session->se_client; __be32 ret = nfserr_bad_stateid; nfs4_lock_state(); - s = find_stateid(stateid); + s = find_stateid(cl, stateid); if (!s) goto out; switch (s->sc_type) { @@ -3419,15 +3421,6 @@ setlkflg (int type) RD_STATE : WR_STATE; } -static __be32 nfs4_nospecial_stateid_checks(stateid_t *stateid) -{ - if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) - return nfserr_bad_stateid; - if (STALE_STATEID(stateid)) - return nfserr_stale_stateid; - return nfs_ok; -} - static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_t *stateid, u32 seqid, struct nfs4_ol_stateid *stp) { struct svc_fh *current_fh = &cstate->current_fh; @@ -3458,17 +3451,16 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, struct nfs4_ol_stateid **stpp) { __be32 status; + struct nfs4_stid *s; dprintk("NFSD: %s: seqid=%d stateid = " STATEID_FMT "\n", __func__, seqid, STATEID_VAL(stateid)); *stpp = NULL; - status = nfs4_nospecial_stateid_checks(stateid); + status = nfsd4_lookup_stateid(stateid, typemask, &s); if (status) return status; - *stpp = find_ol_stateid_by_type(stateid, typemask); - if (*stpp == NULL) - return nfserr_expired; + *stpp = openlockstateid(s); cstate->replay_owner = (*stpp)->st_stateowner; renew_client((*stpp)->st_stateowner->so_client); @@ -3673,6 +3665,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct nfs4_delegation *dp; stateid_t *stateid = &dr->dr_stateid; + struct nfs4_stid *s; struct inode *inode; __be32 status; @@ -3681,16 +3674,10 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, inode = cstate->current_fh.fh_dentry->d_inode; nfs4_lock_state(); - status = nfserr_bad_stateid; - if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) - goto out; - status = nfserr_stale_stateid; - if (STALE_STATEID(stateid)) - goto out; - status = nfserr_expired; - dp = find_deleg_stateid(stateid); - if (!dp) + status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s); + if (status) goto out; + dp = delegstateid(s); status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate)); if (status) goto out; @@ -4409,7 +4396,6 @@ nfs4_state_init(void) for (i = 0; i < OPEN_OWNER_HASH_SIZE; i++) { INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]); } - idr_init(&stateids); for (i = 0; i < LOCK_HASH_SIZE; i++) { INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 2429fffa31dd..5779acde7e70 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3287,6 +3287,7 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_test_stateid *test_stateid) { struct nfsd4_compoundargs *argp; + struct nfs4_client *cl = resp->cstate.session->se_client; stateid_t si; __be32 *p; int i; @@ -3302,7 +3303,7 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr, nfs4_lock_state(); for (i = 0; i < test_stateid->ts_num_ids; i++) { nfsd4_decode_stateid(argp, &si); - valid = nfs4_validate_stateid(&si); + valid = nfs4_validate_stateid(cl, &si); RESERVE_SPACE(4); *p++ = htonl(valid); resp->p = p; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 55a4d6a108a2..13f6f9f5ceec 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -35,6 +35,7 @@ #ifndef _NFSD4_STATE_H #define _NFSD4_STATE_H +#include #include #include #include "nfsfh.h" @@ -231,6 +232,7 @@ struct nfs4_client { struct list_head cl_idhash; /* hash by cl_clientid.id */ struct list_head cl_strhash; /* hash by cl_name */ struct list_head cl_openowners; + struct idr cl_stateids; /* stateid lookup */ struct list_head cl_delegations; struct list_head cl_lru; /* tail queue */ struct xdr_netobj cl_name; /* id generated by client */ @@ -508,7 +510,7 @@ extern void nfsd4_recdir_purge_old(void); extern int nfsd4_create_clid_dir(struct nfs4_client *clp); extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); extern void release_session_client(struct nfsd4_session *); -extern __be32 nfs4_validate_stateid(stateid_t *); +extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *); extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); #endif /* NFSD4_STATE_H */ From 71c3bcd71393a9e67d5b77597a612537f89c30ed Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 27 Sep 2011 21:42:29 -0400 Subject: [PATCH 079/103] nfsd4: fix state lock usage in LOCKU In commit 5ec094c1096ab3bb795651855d53f18daa26afde "nfsd4: extend state lock over seqid replay logic" I modified the exit logic of all the seqid-based procedures except nfsd4_locku(). Fix the oversight. The result of the bug was a double-unlock while handling the LOCKU procedure, and a warning like: [ 142.150014] WARNING: at kernel/mutex-debug.c:78 debug_mutex_unlock+0xda/0xe0() ... [ 142.152927] Pid: 742, comm: nfsd Not tainted 3.1.0-rc1-SLIM+ #9 [ 142.152927] Call Trace: [ 142.152927] [] warn_slowpath_common+0x7f/0xc0 [ 142.152927] [] warn_slowpath_null+0x1a/0x20 [ 142.152927] [] debug_mutex_unlock+0xda/0xe0 [ 142.152927] [] __mutex_unlock_slowpath+0x80/0x140 [ 142.152927] [] mutex_unlock+0xe/0x10 [ 142.152927] [] nfs4_lock_state+0x35/0x40 [nfsd] [ 142.152927] [] nfsd4_proc_compound+0x2a1/0x690 [nfsd] [ 142.152927] [] nfsd_dispatch+0xeb/0x230 [nfsd] [ 142.152927] [] svc_process_common+0x345/0x690 [sunrpc] [ 142.152927] [] ? try_to_wake_up+0x280/0x280 [ 142.152927] [] svc_process+0x102/0x150 [sunrpc] [ 142.152927] [] nfsd+0xbd/0x160 [nfsd] [ 142.152927] [] ? 0xffffffffa039efff [ 142.152927] [] kthread+0x8c/0xa0 [ 142.152927] [] kernel_thread_helper+0x4/0x10 [ 142.152927] [] ? kthread_worker_fn+0x190/0x190 [ 142.152927] [] ? gs_change+0x13/0x13 Reported-by: Bryan Schumaker Tested-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 931155f51ecc..8e253a347649 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4185,7 +4185,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); out: - nfs4_unlock_state(); + if (!cstate->replay_owner) + nfs4_unlock_state(); return status; out_nfserr: From 6409a5a65d2b959c3f5e2b8adfa67c349e294652 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 28 Sep 2011 11:37:56 -0400 Subject: [PATCH 080/103] nfsd4: clean up downgrading code In response to some review comments, get rid of the somewhat obscure for-loop with bitops, and improve a comment. Reported-by: Steve Dickson Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 31 ++++++++++++++++++++++--------- fs/nfsd/state.h | 8 +++++--- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8e253a347649..683885b18ceb 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3523,16 +3523,29 @@ out: return status; } -static inline void nfs4_file_downgrade(struct nfs4_ol_stateid *stp, unsigned int to_access) +static inline void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 access) { - int i; + if (!test_bit(access, &stp->st_access_bmap)) + return; + nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(access)); + __clear_bit(access, &stp->st_access_bmap); +} - for (i = 1; i < 4; i++) { - if (test_bit(i, &stp->st_access_bmap) - && ((i & to_access) != i)) { - nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(i)); - __clear_bit(i, &stp->st_access_bmap); - } +static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_access) +{ + switch (to_access) { + case NFS4_SHARE_ACCESS_READ: + nfs4_stateid_downgrade_bit(stp, NFS4_SHARE_ACCESS_WRITE); + nfs4_stateid_downgrade_bit(stp, NFS4_SHARE_ACCESS_BOTH); + break; + case NFS4_SHARE_ACCESS_WRITE: + nfs4_stateid_downgrade_bit(stp, NFS4_SHARE_ACCESS_READ); + nfs4_stateid_downgrade_bit(stp, NFS4_SHARE_ACCESS_BOTH); + break; + case NFS4_SHARE_ACCESS_BOTH: + break; + default: + BUG(); } } @@ -3578,7 +3591,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, stp->st_deny_bmap, od->od_share_deny); goto out; } - nfs4_file_downgrade(stp, od->od_share_access); + nfs4_stateid_downgrade(stp, od->od_share_access); reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 13f6f9f5ceec..22b065a55ea0 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -403,9 +403,11 @@ struct nfs4_file { /* One each for O_RDONLY, O_WRONLY, O_RDWR: */ struct file * fi_fds[3]; /* - * Each open or lock stateid contributes 1 to either - * fi_access[O_RDONLY], fi_access[O_WRONLY], or both, depending - * on open or lock mode: + * Each open or lock stateid contributes 0-4 to the counts + * below depending on which bits are set in st_access_bitmap: + * 1 to fi_access[O_RDONLY] if NFS4_SHARE_ACCES_READ is set + * + 1 to fi_access[O_WRONLY] if NFS4_SHARE_ACCESS_WRITE is set + * + 1 to both of the above if NFS4_SHARE_ACCESS_BOTH is set. */ atomic_t fi_access[2]; struct file *fi_deleg_file; From b31b30e5c76b7653b4434fcdc3c5d2b46a367c2a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 28 Sep 2011 11:47:20 -0400 Subject: [PATCH 081/103] nfsd4: cleanup state.h comments These comments are mostly out of date. Reported-by: Bryan Schumaker --- fs/nfsd/state.h | 45 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 22b065a55ea0..aa14f06af2df 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -332,25 +332,6 @@ struct nfs4_replay { char rp_ibuf[NFSD4_REPLAY_ISIZE]; }; -/* -* nfs4_stateowner can either be an open_owner, or a lock_owner -* -* so_idhash: stateid_hashtbl[] for open owner, lockstateid_hashtbl[] -* for lock_owner -* so_strhash: ownerstr_hashtbl[] for open_owner, lock_ownerstr_hashtbl[] -* for lock_owner -* so_perclient: nfs4_client->cl_perclient entry - used when nfs4_client -* struct is reaped. -* so_perfilestate: heads the list of nfs4_ol_stateid (either open or lock) -* and is used to ensure no dangling nfs4_ol_stateid references when we -* release a stateowner. -* so_perlockowner: (open) nfs4_ol_stateid->st_perlockowner entry - used when -* close is called to reap associated byte-range locks -* so_close_lru: (open) stateowner is placed on this list instead of being -* reaped (when so_perfilestate is empty) to hold the last close replay. -* reaped by laundramat thread after lease period. -*/ - struct nfs4_stateowner { struct list_head so_strhash; /* hash by op_name */ struct list_head so_stateids; @@ -366,7 +347,14 @@ struct nfs4_stateowner { struct nfs4_openowner { struct nfs4_stateowner oo_owner; /* must be first field */ struct list_head oo_perclient; - struct list_head oo_close_lru; /* tail queue */ + /* + * We keep around openowners a little while after last close, + * which saves clients from having to confirm, and allows us to + * handle close replays if they come soon enough. The close_lru + * is a list of such openowners, to be reaped by the laundromat + * thread eventually if they remain unused: + */ + struct list_head oo_close_lru; struct nfs4_ol_stateid *oo_last_closed_stid; time_t oo_time; /* time of placement on so_close_lru */ #define NFS4_OO_CONFIRMED 1 @@ -443,23 +431,6 @@ static inline struct file *find_any_file(struct nfs4_file *f) return f->fi_fds[O_RDONLY]; } -/* -* nfs4_ol_stateid can either be an open stateid or (eventually) a lock stateid -* -* (open)nfs4_ol_stateid: one per (open)nfs4_stateowner, nfs4_file -* -* st_hash: stateid_hashtbl[] entry or lockstateid_hashtbl entry -* st_perfile: file_hashtbl[] entry. -* st_perfile_state: nfs4_stateowner->so_perfilestate -* st_perlockowner: (open stateid) list of lock nfs4_stateowners -* st_access_bmap: used only for open stateid -* st_deny_bmap: used only for open stateid -* st_openstp: open stateid lock stateid was derived from -* -* XXX: open stateids and lock stateids have diverged sufficiently that -* we should consider defining separate structs for the two cases. -*/ - /* "ol" stands for "Open or Lock". Better suggestions welcome. */ struct nfs4_ol_stateid { struct nfs4_stid st_stid; From dcbf8c30344dfa01915c34f88318d464e116f004 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Sat, 2 Jul 2011 00:47:12 +0200 Subject: [PATCH 082/103] sunrpc: add MODULE_ALIAS to match the filesystem name sunrpc implements the rpc_pipefs filesystem type. Add the alias to have the module requested automatically by the kernel when the filesystem is mounted. Signed-off-by: Michal Schmidt Signed-off-by: J. Bruce Fields --- net/sunrpc/rpc_pipe.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index b181e3441323..3156ba864519 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1084,3 +1084,6 @@ void unregister_rpc_pipefs(void) kmem_cache_destroy(rpc_inode_cachep); unregister_filesystem(&rpc_pipe_fs_type); } + +/* Make 'mount -t rpc_pipefs ...' autoload this module. */ +MODULE_ALIAS("rpc_pipefs"); From c30e92df30d7d5fe65262fbce5d1b7de675fe34e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 10 Oct 2011 17:34:31 -0400 Subject: [PATCH 083/103] nfsd4: ignore WANT bits in open downgrade We don't use WANT bits yet--and sending them can probably trigger a BUG() further down. Cc: stable@kernel.org Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 683885b18ceb..a39a542d8be5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3574,6 +3574,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, if (!access_valid(od->od_share_access, cstate->minorversion) || !deny_valid(od->od_share_deny)) return nfserr_inval; + /* We don't yet support WANT bits: */ + od->od_share_access &= NFS4_SHARE_ACCESS_MASK; nfs4_lock_state(); status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid, From 04f9e664b21c4440daf4d08f31db9b18517e4b8d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 10 Oct 2011 14:37:13 -0400 Subject: [PATCH 084/103] nfsd4: move access/deny validity checks to xdr code I'd rather put more of these sorts of checks into standardized xdr decoders for the various types rather than have them cluttering up the core logic in nfs4proc.c and nfs4state.c. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 32 ------------------ fs/nfsd/nfs4xdr.c | 80 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 39 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a39a542d8be5..6bfa293e1c91 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2418,31 +2418,6 @@ find_file(struct inode *ino) return NULL; } -static inline int access_valid(u32 x, u32 minorversion) -{ - if ((x & NFS4_SHARE_ACCESS_MASK) < NFS4_SHARE_ACCESS_READ) - return 0; - if ((x & NFS4_SHARE_ACCESS_MASK) > NFS4_SHARE_ACCESS_BOTH) - return 0; - x &= ~NFS4_SHARE_ACCESS_MASK; - if (minorversion && x) { - if ((x & NFS4_SHARE_WANT_MASK) > NFS4_SHARE_WANT_CANCEL) - return 0; - if ((x & NFS4_SHARE_WHEN_MASK) > NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED) - return 0; - x &= ~(NFS4_SHARE_WANT_MASK | NFS4_SHARE_WHEN_MASK); - } - if (x) - return 0; - return 1; -} - -static inline int deny_valid(u32 x) -{ - /* Note: unlike access bits, deny bits may be zero. */ - return x <= NFS4_SHARE_DENY_BOTH; -} - /* * Called to check deny when READ with all zero stateid or * WRITE with all zero or all one stateid @@ -2918,10 +2893,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf struct nfs4_delegation *dp = NULL; __be32 status; - status = nfserr_inval; - if (!access_valid(open->op_share_access, resp->cstate.minorversion) - || !deny_valid(open->op_share_deny)) - goto out; /* * Lookup file; if found, lookup stateid and check open request, * and check for delegations in the process of being recalled. @@ -3571,9 +3542,6 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, (int)cstate->current_fh.fh_dentry->d_name.len, cstate->current_fh.fh_dentry->d_name.name); - if (!access_valid(od->od_share_access, cstate->minorversion) - || !deny_valid(od->od_share_deny)) - return nfserr_inval; /* We don't yet support WANT bits: */ od->od_share_access &= NFS4_SHARE_ACCESS_MASK; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 5779acde7e70..94da8bb36c85 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -639,6 +639,64 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup DECODE_TAIL; } +static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *x) +{ + __be32 *p; + u32 w; + + READ_BUF(4); + READ32(w); + *x = w; + switch (w & NFS4_SHARE_ACCESS_MASK) { + case NFS4_SHARE_ACCESS_READ: + case NFS4_SHARE_ACCESS_WRITE: + case NFS4_SHARE_ACCESS_BOTH: + break; + default: + return nfserr_bad_xdr; + } + w &= !NFS4_SHARE_ACCESS_MASK; + if (!w) + return nfs_ok; + if (!argp->minorversion) + return nfserr_bad_xdr; + switch (w & NFS4_SHARE_WANT_MASK) { + case NFS4_SHARE_WANT_NO_PREFERENCE: + case NFS4_SHARE_WANT_READ_DELEG: + case NFS4_SHARE_WANT_WRITE_DELEG: + case NFS4_SHARE_WANT_ANY_DELEG: + case NFS4_SHARE_WANT_NO_DELEG: + case NFS4_SHARE_WANT_CANCEL: + break; + default: + return nfserr_bad_xdr; + } + w &= !NFS4_SHARE_WANT_MASK; + if (!w) + return nfs_ok; + switch (w) { + case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL: + case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED: + return nfs_ok; + } +xdr_error: + return nfserr_bad_xdr; +} + +static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x) +{ + __be32 *p; + + READ_BUF(4); + READ32(*x); + /* Note: unlinke access bits, deny bits may be zero. */ + if (*x & !NFS4_SHARE_DENY_BOTH) + return nfserr_bad_xdr; + return nfs_ok; +xdr_error: + return nfserr_bad_xdr; +} + static __be32 nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) { @@ -649,10 +707,15 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) open->op_openowner = NULL; /* seqid, share_access, share_deny, clientid, ownerlen */ - READ_BUF(16 + sizeof(clientid_t)); + READ_BUF(4); READ32(open->op_seqid); - READ32(open->op_share_access); - READ32(open->op_share_deny); + status = nfsd4_decode_share_access(argp, &open->op_share_access); + if (status) + goto xdr_error; + status = nfsd4_decode_share_deny(argp, &open->op_share_deny); + if (status) + goto xdr_error; + READ_BUF(sizeof(clientid_t) + 4); COPYMEM(&open->op_clientid, sizeof(clientid_t)); READ32(open->op_owner.len); @@ -753,11 +816,14 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d status = nfsd4_decode_stateid(argp, &open_down->od_stateid); if (status) return status; - READ_BUF(12); + READ_BUF(4); READ32(open_down->od_seqid); - READ32(open_down->od_share_access); - READ32(open_down->od_share_deny); - + status = nfsd4_decode_share_access(argp, &open_down->od_share_access); + if (status) + return status; + status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny); + if (status) + return status; DECODE_TAIL; } From a084daf512bb66fa3c8e21c7027daea521179cd0 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 10 Oct 2011 15:07:40 -0400 Subject: [PATCH 085/103] nfsd4: move name-length checks to xdr Again, these checks are better in the xdr code. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 24 ++---------------------- fs/nfsd/nfs4xdr.c | 45 ++++++++++++++++++++++++++++++--------------- fs/nfsd/xdr4.h | 3 +-- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6bfa293e1c91..5f35f35a2da0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1189,17 +1189,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, return clp; } -static int check_name(struct xdr_netobj name) -{ - if (name.len == 0) - return 0; - if (name.len > NFS4_OPAQUE_LIMIT) { - dprintk("NFSD: check_name: name too long(%d)!\n", name.len); - return 0; - } - return 1; -} - static void add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) { @@ -1442,7 +1431,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, __func__, rqstp, exid, exid->clname.len, exid->clname.data, addr_str, exid->flags, exid->spa_how); - if (!check_name(exid->clname) || (exid->flags & ~EXCHGID4_FLAG_MASK_A)) + if (exid->flags & ~EXCHGID4_FLAG_MASK_A) return nfserr_inval; /* Currently only support SP4_NONE */ @@ -1992,19 +1981,13 @@ __be32 nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_setclientid *setclid) { - struct xdr_netobj clname = { - .len = setclid->se_namelen, - .data = setclid->se_name, - }; + struct xdr_netobj clname = setclid->se_name; nfs4_verifier clverifier = setclid->se_verf; unsigned int strhashval; struct nfs4_client *conf, *unconf, *new; __be32 status; char dname[HEXDIR_LEN]; - if (!check_name(clname)) - return nfserr_inval; - status = nfs4_make_rec_clidname(dname, &clname); if (status) return status; @@ -2523,9 +2506,6 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, struct nfs4_openowner *oo = NULL; __be32 status; - if (!check_name(open->op_owner)) - return nfserr_inval; - if (STALE_CLIENTID(&open->op_clientid)) return nfserr_stale_clientid; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 94da8bb36c85..2cab33cc3238 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -697,6 +697,23 @@ xdr_error: return nfserr_bad_xdr; } +static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o) +{ + __be32 *p; + + READ_BUF(4); + READ32(o->len); + + if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT) + return nfserr_bad_xdr; + + READ_BUF(o->len); + SAVEMEM(o->data, o->len); + return nfs_ok; +xdr_error: + return nfserr_bad_xdr; +} + static __be32 nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) { @@ -715,13 +732,12 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) status = nfsd4_decode_share_deny(argp, &open->op_share_deny); if (status) goto xdr_error; - READ_BUF(sizeof(clientid_t) + 4); + READ_BUF(sizeof(clientid_t)); COPYMEM(&open->op_clientid, sizeof(clientid_t)); - READ32(open->op_owner.len); - - /* owner, open_flag */ - READ_BUF(open->op_owner.len + 4); - SAVEMEM(open->op_owner.data, open->op_owner.len); + status = nfsd4_decode_opaque(argp, &open->op_owner); + if (status) + goto xdr_error; + READ_BUF(4); READ32(open->op_create); switch (open->op_create) { case NFS4_OPEN_NOCREATE: @@ -964,12 +980,13 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient { DECODE_HEAD; - READ_BUF(12); + READ_BUF(8); COPYMEM(setclientid->se_verf.data, 8); - READ32(setclientid->se_namelen); - READ_BUF(setclientid->se_namelen + 8); - SAVEMEM(setclientid->se_name, setclientid->se_namelen); + status = nfsd4_decode_opaque(argp, &setclientid->se_name); + if (status) + return nfserr_bad_xdr; + READ_BUF(8); READ32(setclientid->se_callback_prog); READ32(setclientid->se_callback_netid_len); @@ -1112,11 +1129,9 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, READ_BUF(NFS4_VERIFIER_SIZE); COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE); - READ_BUF(4); - READ32(exid->clname.len); - - READ_BUF(exid->clname.len); - SAVEMEM(exid->clname.data, exid->clname.len); + status = nfsd4_decode_opaque(argp, &exid->clname); + if (status) + return nfserr_bad_xdr; READ_BUF(4); READ32(exid->flags); diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index c9012149637c..4c8a7ec3f25d 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -317,8 +317,7 @@ struct nfsd4_setattr { struct nfsd4_setclientid { nfs4_verifier se_verf; /* request */ - u32 se_namelen; /* request */ - char * se_name; /* request */ + struct xdr_netobj se_name; u32 se_callback_prog; /* request */ u32 se_callback_netid_len; /* request */ char * se_callback_netid_val; /* request */ From b6d2f1ca3c1162f51098969e9c52fd099720416a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 10 Oct 2011 17:44:19 -0400 Subject: [PATCH 086/103] nfsd4: more robust ignoring of WANT bits in OPEN Mask out the WANT bits right at the start instead of on each use. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 3 +++ fs/nfsd/nfs4state.c | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 4e41f65c7021..5b192a2512b6 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -315,6 +315,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) return nfserr_inval; + /* We don't yet support WANT bits: */ + open->op_share_access &= NFS4_SHARE_ACCESS_MASK; + /* * RFC5661 18.51.3 * Before RECLAIM_COMPLETE done, server should deny new lock diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 5f35f35a2da0..2042805da960 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2345,8 +2345,7 @@ static inline __be32 init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_ stp->st_file = fp; stp->st_access_bmap = 0; stp->st_deny_bmap = 0; - __set_bit(open->op_share_access & ~NFS4_SHARE_WANT_MASK, - &stp->st_access_bmap); + __set_bit(open->op_share_access, &stp->st_access_bmap); __set_bit(open->op_share_deny, &stp->st_deny_bmap); stp->st_openstp = NULL; return nfs_ok; @@ -2690,7 +2689,7 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, static __be32 nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open) { - u32 op_share_access = open->op_share_access & ~NFS4_SHARE_WANT_MASK; + u32 op_share_access = open->op_share_access; bool new_access; __be32 status; From 5703728ac1194baee554163180415baf4ccd9996 Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Wed, 12 Oct 2011 15:09:34 +0800 Subject: [PATCH 087/103] nfs: fix bug about IPv6 address scope checking The result from ipv6_addr_scope() is a set of flags, not a single value, so we can't just compare the result with IPV6_ADDR_SCOPE_LINKLOCAL. This patch fixs the problem, and checks for unequal addresses before scope_id. Signed-off-by: Mi Jinlong Signed-off-by: J. Bruce Fields --- fs/nfs/client.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 5833fbbf59b0..b4e41dd4d0f6 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -336,11 +336,12 @@ static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; - if (ipv6_addr_scope(&sin1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL && - sin1->sin6_scope_id != sin2->sin6_scope_id) + if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr)) return 0; + else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL) + return sin1->sin6_scope_id == sin2->sin6_scope_id; - return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr); + return 1; } #else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */ static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, From 01cd4afadbf376de07d364a632cc82a0fc5e8655 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 17 Oct 2011 10:41:17 +0300 Subject: [PATCH 088/103] nfsd4: typo logical vs bitwise negate This should be a bitwise negate here. It silences a Sparse warning: fs/nfsd/nfs4xdr.c:693:16: warning: dubious: x & !y Signed-off-by: Dan Carpenter Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 2cab33cc3238..645a0a9d8073 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -690,7 +690,7 @@ static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x) READ_BUF(4); READ32(*x); /* Note: unlinke access bits, deny bits may be zero. */ - if (*x & !NFS4_SHARE_DENY_BOTH) + if (*x & ~NFS4_SHARE_DENY_BOTH) return nfserr_bad_xdr; return nfs_ok; xdr_error: From a50d2ad1721c0c785e9a74c0003ca044de6868a5 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 12 Oct 2011 16:24:27 -0400 Subject: [PATCH 089/103] nfsd4: centralize renew_client() calls There doesn't seem to be any harm to renewing the client a bit earlier, when it is looked up. That saves us from having to sprinkle renew_client calls over quite so many places. Also remove a redundant comment and do a little cleanup. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2042805da960..d90461eb9368 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -931,9 +931,6 @@ renew_client_locked(struct nfs4_client *clp) return; } - /* - * Move client to the end to the LRU list. - */ dprintk("renewing client (clientid %08x/%08x)\n", clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); @@ -1220,8 +1217,10 @@ find_confirmed_client(clientid_t *clid) unsigned int idhashval = clientid_hashval(clid->cl_id); list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { - if (same_clid(&clp->cl_clientid, clid)) + if (same_clid(&clp->cl_clientid, clid)) { + renew_client(clp); return clp; + } } return NULL; } @@ -2372,11 +2371,15 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, static struct nfs4_openowner * find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open) { - struct nfs4_stateowner *so = NULL; + struct nfs4_stateowner *so; + struct nfs4_openowner *oo; list_for_each_entry(so, &open_ownerstr_hashtbl[hashval], so_strhash) { - if (same_owner_str(so, &open->op_owner, &open->op_clientid)) - return container_of(so, struct nfs4_openowner, oo_owner); + if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { + oo = openowner(so); + renew_client(oo->oo_owner.so_client); + return oo; + } } return NULL; } @@ -2536,7 +2539,6 @@ renew: open->op_openowner = oo; } list_del_init(&oo->oo_close_lru); - renew_client(oo->oo_owner.so_client); return nfs_ok; } @@ -2970,7 +2972,6 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, dprintk("nfsd4_renew: clientid not found!\n"); goto out; } - renew_client(clp); status = nfserr_cb_path_down; if (!list_empty(&clp->cl_delegations) && clp->cl_cb_state != NFSD4_CB_UP) @@ -3275,7 +3276,6 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, status = nfs4_check_delegmode(dp, flags); if (status) goto out; - renew_client(dp->dl_stid.sc_client); if (filpp) { *filpp = dp->dl_file->fi_deleg_file; BUG_ON(!*filpp); @@ -3293,7 +3293,6 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, status = nfs4_check_openmode(stp, flags); if (status) goto out; - renew_client(stp->st_stateowner->so_client); if (filpp) { if (flags & RD_STATE) *filpp = find_readable_file(stp->st_file); @@ -3412,7 +3411,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, return status; *stpp = openlockstateid(s); cstate->replay_owner = (*stpp)->st_stateowner; - renew_client((*stpp)->st_stateowner->so_client); return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp); } @@ -3643,7 +3641,6 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate)); if (status) goto out; - renew_client(dp->dl_stid.sc_client); unhash_delegation(dp); out: From 3557e43b8f78e5c2347bab31626fdb4d09220ae7 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 12 Oct 2011 16:58:21 -0400 Subject: [PATCH 090/103] nfsd4: make is_open_owner boolean Signed-off-by: J. Bruce Fields --- fs/nfsd/state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index aa14f06af2df..87eecfd9b968 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -335,13 +335,13 @@ struct nfs4_replay { struct nfs4_stateowner { struct list_head so_strhash; /* hash by op_name */ struct list_head so_stateids; - int so_is_open_owner; /* 1=openowner,0=lockowner */ struct nfs4_client * so_client; /* after increment in ENCODE_SEQID_OP_TAIL, represents the next * sequence id expected from the client: */ u32 so_seqid; struct xdr_netobj so_owner; /* open owner name */ struct nfs4_replay so_replay; + bool so_is_open_owner; }; struct nfs4_openowner { From bcf130f9dfbaccf91376a44b18f51ed8007840d6 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 12 Oct 2011 20:44:20 -0400 Subject: [PATCH 091/103] nfsd4: simplify process_open1 logic No change in behavior. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d90461eb9368..62aa91ae278b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2506,7 +2506,6 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, struct nfs4_client *clp = NULL; unsigned int strhashval; struct nfs4_openowner *oo = NULL; - __be32 status; if (STALE_CLIENTID(&open->op_clientid)) return nfserr_stale_clientid; @@ -2515,30 +2514,25 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, oo = find_openstateowner_str(strhashval, open); open->op_openowner = oo; if (!oo) { - /* Make sure the client's lease hasn't expired. */ clp = find_confirmed_client(clientid); if (clp == NULL) return nfserr_expired; - goto renew; + goto new_owner; } if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) { /* Replace unconfirmed owners without checking for replay. */ clp = oo->oo_owner.so_client; release_openowner(oo); open->op_openowner = NULL; - goto renew; - } - status = nfsd4_check_seqid(cstate, &oo->oo_owner, open->op_seqid); - if (status) - return status; -renew: - if (open->op_openowner == NULL) { - oo = alloc_init_open_stateowner(strhashval, clp, open); - if (oo == NULL) - return nfserr_jukebox; - open->op_openowner = oo; + goto new_owner; } list_del_init(&oo->oo_close_lru); + return nfsd4_check_seqid(cstate, &oo->oo_owner, open->op_seqid); +new_owner: + oo = alloc_init_open_stateowner(strhashval, clp, open); + if (oo == NULL) + return nfserr_jukebox; + open->op_openowner = oo; return nfs_ok; } From d29b20cd589128a599e5045d4effc2d7dbc388f5 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 13 Oct 2011 15:12:59 -0400 Subject: [PATCH 092/103] nfsd4: clean up open owners on OPEN failure If process_open1() creates a new open owner, but the open later fails, the current code will leave the open owner around. It won't be on the close_lru list, and the client isn't expected to send a CLOSE, so it will hang around as long as the client does. Similarly, if process_open1() removes an existing open owner from the close lru, anticipating that an open owner that previously had no associated stateid's now will, but the open subsequently fails, then we'll again be left with the same leak. Fix both problems. Reported-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 1 + fs/nfsd/nfs4state.c | 20 ++++++++++++++++++-- fs/nfsd/state.h | 1 + fs/nfsd/xdr4.h | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 5b192a2512b6..10b50d78bdc3 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -409,6 +409,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, */ status = nfsd4_process_open2(rqstp, &cstate->current_fh, open); out: + nfsd4_cleanup_open_state(open, status); if (open->op_openowner) cstate->replay_owner = &open->op_openowner->oo_owner; else diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 62aa91ae278b..2c9a1a20e014 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2320,7 +2320,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str return NULL; oo->oo_owner.so_is_open_owner = 1; oo->oo_owner.so_seqid = open->op_seqid; - oo->oo_flags = 0; + oo->oo_flags = NFS4_OO_NEW; oo->oo_time = 0; oo->oo_last_closed_stid = NULL; INIT_LIST_HEAD(&oo->oo_close_lru); @@ -2526,7 +2526,6 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, open->op_openowner = NULL; goto new_owner; } - list_del_init(&oo->oo_close_lru); return nfsd4_check_seqid(cstate, &oo->oo_owner, open->op_seqid); new_owner: oo = alloc_init_open_stateowner(strhashval, clp, open); @@ -2946,6 +2945,23 @@ out: return status; } +void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) +{ + if (open->op_openowner) { + struct nfs4_openowner *oo = open->op_openowner; + + if (!list_empty(&oo->oo_owner.so_stateids)) + list_del_init(&oo->oo_close_lru); + if (oo->oo_flags & NFS4_OO_NEW) { + if (status) { + release_openowner(oo); + open->op_openowner = NULL; + } else + oo->oo_flags &= ~NFS4_OO_NEW; + } + } +} + __be32 nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, clientid_t *clid) diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 87eecfd9b968..eab9dae23c06 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -359,6 +359,7 @@ struct nfs4_openowner { time_t oo_time; /* time of placement on so_close_lru */ #define NFS4_OO_CONFIRMED 1 #define NFS4_OO_PURGE_CLOSE 2 +#define NFS4_OO_NEW 4 unsigned char oo_flags; }; diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 4c8a7ec3f25d..32e6fd8d9768 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -554,6 +554,7 @@ extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, struct nfsd4_open *open); extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open); +extern void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status); extern __be32 nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *, struct nfsd4_open_confirm *oc); extern __be32 nfsd4_close(struct svc_rqst *rqstp, From 32513b40efdc693b3675f1c691ab901518fbcb6a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 13 Oct 2011 16:00:16 -0400 Subject: [PATCH 093/103] nfsd4: preallocate nfs4_file in process_open1() Creating a new file is an irrevocable step--once it's visible in the filesystem, other processes may have seen it and done something with it, and unlinking it wouldn't simply undo the effects of the create. Therefore, in the case where OPEN creates a new file, we shouldn't do the create until we know that the rest of the OPEN processing will succeed. For example, we should preallocate a struct file in case we need it until waiting to allocate it till process_open2(), which is already too late. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 62 +++++++++++++++++++++++++++------------------ fs/nfsd/xdr4.h | 1 + 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2c9a1a20e014..ae5d25075f67 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -104,6 +104,11 @@ opaque_hashval(const void *ptr, int nbytes) static struct list_head del_recall_lru; +static void nfsd4_free_file(struct nfs4_file *f) +{ + kmem_cache_free(file_slab, f); +} + static inline void put_nfs4_file(struct nfs4_file *fi) { @@ -111,7 +116,7 @@ put_nfs4_file(struct nfs4_file *fi) list_del(&fi->fi_hash); spin_unlock(&recall_lock); iput(fi->fi_inode); - kmem_cache_free(file_slab, fi); + nfsd4_free_file(fi); } } @@ -2190,30 +2195,28 @@ out: return status; } -/* OPEN Share state helper functions */ -static inline struct nfs4_file * -alloc_init_file(struct inode *ino) +static struct nfs4_file *nfsd4_alloc_file(void) +{ + return kmem_cache_alloc(file_slab, GFP_KERNEL); +} + +/* OPEN Share state helper functions */ +static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino) { - struct nfs4_file *fp; unsigned int hashval = file_hashval(ino); - fp = kmem_cache_alloc(file_slab, GFP_KERNEL); - if (fp) { - atomic_set(&fp->fi_ref, 1); - INIT_LIST_HEAD(&fp->fi_hash); - INIT_LIST_HEAD(&fp->fi_stateids); - INIT_LIST_HEAD(&fp->fi_delegations); - fp->fi_inode = igrab(ino); - fp->fi_had_conflict = false; - fp->fi_lease = NULL; - memset(fp->fi_fds, 0, sizeof(fp->fi_fds)); - memset(fp->fi_access, 0, sizeof(fp->fi_access)); - spin_lock(&recall_lock); - list_add(&fp->fi_hash, &file_hashtbl[hashval]); - spin_unlock(&recall_lock); - return fp; - } - return NULL; + atomic_set(&fp->fi_ref, 1); + INIT_LIST_HEAD(&fp->fi_hash); + INIT_LIST_HEAD(&fp->fi_stateids); + INIT_LIST_HEAD(&fp->fi_delegations); + fp->fi_inode = igrab(ino); + fp->fi_had_conflict = false; + fp->fi_lease = NULL; + memset(fp->fi_fds, 0, sizeof(fp->fi_fds)); + memset(fp->fi_access, 0, sizeof(fp->fi_access)); + spin_lock(&recall_lock); + list_add(&fp->fi_hash, &file_hashtbl[hashval]); + spin_unlock(&recall_lock); } static void @@ -2509,6 +2512,13 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, if (STALE_CLIENTID(&open->op_clientid)) return nfserr_stale_clientid; + /* + * In case we need it later, after we've already created the + * file and don't want to risk a further failure: + */ + open->op_file = nfsd4_alloc_file(); + if (open->op_file == NULL) + return nfserr_jukebox; strhashval = open_ownerstr_hashval(clientid->cl_id, &open->op_owner); oo = find_openstateowner_str(strhashval, open); @@ -2884,9 +2894,9 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) goto out; status = nfserr_jukebox; - fp = alloc_init_file(ino); - if (fp == NULL) - goto out; + fp = open->op_file; + open->op_file = NULL; + nfsd4_init_file(fp, ino); } /* @@ -2960,6 +2970,8 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) oo->oo_flags &= ~NFS4_OO_NEW; } } + if (open->op_file) + nfsd4_free_file(open->op_file); } __be32 diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 32e6fd8d9768..502dd43634f9 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -228,6 +228,7 @@ struct nfsd4_open { u32 op_rflags; /* response */ int op_truncate; /* used during processing */ struct nfs4_openowner *op_openowner; /* used during processing */ + struct nfs4_file *op_file; /* used during processing */ struct nfs4_acl *op_acl; }; #define op_iattr iattr From 996e09385c364f97a89648b401409521e2a3a094 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 17 Oct 2011 11:14:48 -0400 Subject: [PATCH 094/103] nfsd4: do idr preallocation with stateid allocation Move idr preallocation out of stateid initialization, into stateid allocation, so that we no longer have to handle any errors from the former. This is a little subtle due to the way the idr code manages these preallocated items--document that in comments. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 76 ++++++++++++++++++++++----------------------- fs/nfsd/state.h | 4 +-- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ae5d25075f67..1f8c781c2a28 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -215,13 +215,12 @@ static inline int get_new_stid(struct nfs4_stid *stid) int new_stid; int error; - if (!idr_pre_get(stateids, GFP_KERNEL)) - return -ENOMEM; - error = idr_get_new_above(stateids, stid, min_stateid, &new_stid); /* - * All this code is currently serialized; the preallocation - * above should still be ours: + * Note: the necessary preallocation was done in + * nfs4_alloc_stateid(). The idr code caps the number of + * preallocations that can exist at a time, but the state lock + * prevents anyone from using ours before we get here: */ BUG_ON(error); /* @@ -240,7 +239,7 @@ static inline int get_new_stid(struct nfs4_stid *stid) return new_stid; } -static inline __be32 init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, unsigned char type) +static void init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, unsigned char type) { stateid_t *s = &stid->sc_stateid; int new_id; @@ -249,12 +248,24 @@ static inline __be32 init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, u stid->sc_client = cl; s->si_opaque.so_clid = cl->cl_clientid; new_id = get_new_stid(stid); - if (new_id < 0) - return nfserr_jukebox; s->si_opaque.so_id = (u32)new_id; /* Will be incremented before return to client: */ s->si_generation = 0; - return 0; +} + +static struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab) +{ + struct idr *stateids = &cl->cl_stateids; + + if (!idr_pre_get(stateids, GFP_KERNEL)) + return NULL; + /* + * Note: if we fail here (or any time between now and the time + * we actually get the new idr), we won't need to undo the idr + * preallocation, since the idr code caps the number of + * preallocated entries. + */ + return kmem_cache_alloc(slab, GFP_KERNEL); } static struct nfs4_delegation * @@ -262,7 +273,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv { struct nfs4_delegation *dp; struct nfs4_file *fp = stp->st_file; - __be32 status; dprintk("NFSD alloc_init_deleg\n"); /* @@ -276,14 +286,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv return NULL; if (num_delegations > max_delegations) return NULL; - dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); + dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab)); if (dp == NULL) return dp; - status = init_stid(&dp->dl_stid, clp, NFS4_DELEG_STID); - if (status) { - kmem_cache_free(deleg_slab, dp); - return NULL; - } + init_stid(&dp->dl_stid, clp, NFS4_DELEG_STID); /* * delegation seqid's are never incremented. The 4.1 special * meaning of seqid 0 isn't meaningful, really, but let's avoid @@ -2331,14 +2337,11 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str return oo; } -static inline __be32 init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { +static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { struct nfs4_openowner *oo = open->op_openowner; struct nfs4_client *clp = oo->oo_owner.so_client; - __be32 status; - status = init_stid(&stp->st_stid, clp, NFS4_OPEN_STID); - if (status) - return status; + init_stid(&stp->st_stid, clp, NFS4_OPEN_STID); INIT_LIST_HEAD(&stp->st_lockowners); list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); @@ -2350,7 +2353,6 @@ static inline __be32 init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_ __set_bit(open->op_share_access, &stp->st_access_bmap); __set_bit(open->op_share_deny, &stp->st_deny_bmap); stp->st_openstp = NULL; - return nfs_ok; } static void @@ -2614,10 +2616,14 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st return nfs_ok; } -static inline struct nfs4_ol_stateid * -nfs4_alloc_stateid(void) +static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp) { - return kmem_cache_alloc(stateid_slab, GFP_KERNEL); + return openlockstateid(nfs4_alloc_stid(clp, stateid_slab)); +} + +static void nfs4_free_stateid(struct nfs4_ol_stateid *s) +{ + kmem_cache_free(stateid_slab, s); } static inline int nfs4_access_to_access(u32 nfs4_access) @@ -2661,15 +2667,16 @@ nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_ol_stateid **stpp, struct nfsd4_open *open) { struct nfs4_ol_stateid *stp; + struct nfs4_client *cl = open->op_openowner->oo_owner.so_client; __be32 status; - stp = nfs4_alloc_stateid(); + stp = nfs4_alloc_stateid(cl); if (stp == NULL) return nfserr_jukebox; status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open); if (status) { - kmem_cache_free(stateid_slab, stp); + nfs4_free_stateid(stp); return status; } *stpp = stp; @@ -2912,11 +2919,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status = nfs4_new_open(rqstp, &stp, fp, current_fh, open); if (status) goto out; - status = init_open_stateid(stp, fp, open); - if (status) { - release_open_stateid(stp); - goto out; - } + init_open_stateid(stp, fp, open); status = nfsd4_truncate(rqstp, current_fh, open); if (status) { release_open_stateid(stp); @@ -3812,16 +3815,11 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct { struct nfs4_ol_stateid *stp; struct nfs4_client *clp = lo->lo_owner.so_client; - __be32 status; - stp = nfs4_alloc_stateid(); + stp = nfs4_alloc_stateid(clp); if (stp == NULL) return NULL; - status = init_stid(&stp->st_stid, clp, NFS4_LOCK_STID); - if (status) { - free_generic_stateid(stp); - return NULL; - } + init_stid(&stp->st_stid, clp, NFS4_LOCK_STID); list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); stp->st_stateowner = &lo->lo_owner; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index eab9dae23c06..1a5820066040 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -85,6 +85,7 @@ struct nfs4_stid { }; struct nfs4_delegation { + struct nfs4_stid dl_stid; /* must be first field */ struct list_head dl_perfile; struct list_head dl_perclnt; struct list_head dl_recall_lru; /* delegation recalled */ @@ -93,7 +94,6 @@ struct nfs4_delegation { u32 dl_type; time_t dl_time; /* For recall: */ - struct nfs4_stid dl_stid; struct knfsd_fh dl_fh; int dl_retries; struct nfsd4_callback dl_recall; @@ -434,7 +434,7 @@ static inline struct file *find_any_file(struct nfs4_file *f) /* "ol" stands for "Open or Lock". Better suggestions welcome. */ struct nfs4_ol_stateid { - struct nfs4_stid st_stid; + struct nfs4_stid st_stid; /* must be first field */ struct list_head st_perfile; struct list_head st_perstateowner; struct list_head st_lockowners; From 4cdc951b8611de4ce25e35c9fb8c0656150c9245 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 17 Oct 2011 15:57:47 -0400 Subject: [PATCH 095/103] nfsd4: preallocate open stateid in process_open1() As with the nfs4_file, we'd prefer to find out about any failure before creating a new file rather than after. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 49 ++++++++++++++++++--------------------------- fs/nfsd/xdr4.h | 1 + 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1f8c781c2a28..3e1d4e08dfad 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -268,6 +268,11 @@ static struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cac return kmem_cache_alloc(slab, GFP_KERNEL); } +static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp) +{ + return openlockstateid(nfs4_alloc_stid(clp, stateid_slab)); +} + static struct nfs4_delegation * alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh, u32 type) { @@ -2511,6 +2516,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, struct nfs4_client *clp = NULL; unsigned int strhashval; struct nfs4_openowner *oo = NULL; + __be32 status; if (STALE_CLIENTID(&open->op_clientid)) return nfserr_stale_clientid; @@ -2538,12 +2544,20 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, open->op_openowner = NULL; goto new_owner; } - return nfsd4_check_seqid(cstate, &oo->oo_owner, open->op_seqid); + status = nfsd4_check_seqid(cstate, &oo->oo_owner, open->op_seqid); + if (status) + return status; + clp = oo->oo_owner.so_client; + goto alloc_stateid; new_owner: oo = alloc_init_open_stateowner(strhashval, clp, open); if (oo == NULL) return nfserr_jukebox; open->op_openowner = oo; +alloc_stateid: + open->op_stp = nfs4_alloc_stateid(clp); + if (!open->op_stp) + return nfserr_jukebox; return nfs_ok; } @@ -2616,11 +2630,6 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st return nfs_ok; } -static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp) -{ - return openlockstateid(nfs4_alloc_stid(clp, stateid_slab)); -} - static void nfs4_free_stateid(struct nfs4_ol_stateid *s) { kmem_cache_free(stateid_slab, s); @@ -2661,28 +2670,6 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, return nfs_ok; } -static __be32 -nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_ol_stateid **stpp, - struct nfs4_file *fp, struct svc_fh *cur_fh, - struct nfsd4_open *open) -{ - struct nfs4_ol_stateid *stp; - struct nfs4_client *cl = open->op_openowner->oo_owner.so_client; - __be32 status; - - stp = nfs4_alloc_stateid(cl); - if (stp == NULL) - return nfserr_jukebox; - - status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open); - if (status) { - nfs4_free_stateid(stp); - return status; - } - *stpp = stp; - return 0; -} - static inline __be32 nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, struct nfsd4_open *open) @@ -2916,9 +2903,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf if (status) goto out; } else { - status = nfs4_new_open(rqstp, &stp, fp, current_fh, open); + status = nfs4_get_vfs_file(rqstp, fp, current_fh, open); if (status) goto out; + stp = open->op_stp; + open->op_stp = NULL; init_open_stateid(stp, fp, open); status = nfsd4_truncate(rqstp, current_fh, open); if (status) { @@ -2975,6 +2964,8 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) } if (open->op_file) nfsd4_free_file(open->op_file); + if (open->op_stp) + nfs4_free_stateid(open->op_stp); } __be32 diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 502dd43634f9..ce8c59196b4e 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -229,6 +229,7 @@ struct nfsd4_open { int op_truncate; /* used during processing */ struct nfs4_openowner *op_openowner; /* used during processing */ struct nfs4_file *op_file; /* used during processing */ + struct nfs4_ol_stateid *op_stp; /* used during processing */ struct nfs4_acl *op_acl; }; #define op_iattr iattr From 856121b2e83bd64bffdc8de449d24c9295e92ff3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 13 Oct 2011 11:37:11 -0400 Subject: [PATCH 096/103] nfsd4: warn on open failure after create If we create the object and then return failure to the client, we're left with an unexpected file in the filesystem. I'm trying to eliminate such cases but not 100% sure I have so an assertion might be helpful for now. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 7 ++++--- fs/nfsd/vfs.c | 2 +- fs/nfsd/vfs.h | 2 +- fs/nfsd/xdr4.h | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 10b50d78bdc3..710b97b7a2f3 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -194,7 +194,6 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o { struct svc_fh resfh; __be32 status; - int created = 0; fh_init(&resfh, NFS4_FHSIZE); open->op_truncate = 0; @@ -223,7 +222,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o open->op_fname.len, &open->op_iattr, &resfh, open->op_createmode, (u32 *)open->op_verf.data, - &open->op_truncate, &created); + &open->op_truncate, &open->op_created); /* * Following rfc 3530 14.2.16, use the returned bitmask @@ -253,7 +252,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o /* set reply cache */ fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, &resfh.fh_handle); - if (!created) + if (!open->op_created) status = do_open_permission(rqstp, current_fh, open, NFSD_MAY_NOP); @@ -318,6 +317,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* We don't yet support WANT bits: */ open->op_share_access &= NFS4_SHARE_ACCESS_MASK; + open->op_created = 0; /* * RFC5661 18.51.3 * Before RECLAIM_COMPLETE done, server should deny new lock @@ -408,6 +408,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * set, (2) sets open->op_stateid, (3) sets open->op_delegation. */ status = nfsd4_process_open2(rqstp, &cstate->current_fh, open); + WARN_ON(status && open->op_created); out: nfsd4_cleanup_open_state(open, status); if (open->op_openowner) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 4dd91283d039..7a2e442623c8 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1370,7 +1370,7 @@ __be32 do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, char *fname, int flen, struct iattr *iap, struct svc_fh *resfhp, int createmode, u32 *verifier, - int *truncp, int *created) + bool *truncp, bool *created) { struct dentry *dentry, *dchild = NULL; struct inode *dirp; diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 503f3bf11abd..3f54ad03bb2b 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -62,7 +62,7 @@ __be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *); __be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *, char *name, int len, struct iattr *attrs, struct svc_fh *res, int createmode, - u32 *verifier, int *truncp, int *created); + u32 *verifier, bool *truncp, bool *created); __be32 nfsd_commit(struct svc_rqst *, struct svc_fh *, loff_t, unsigned long); #endif /* CONFIG_NFSD_V3 */ diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index ce8c59196b4e..e3057350eea1 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -226,7 +226,8 @@ struct nfsd4_open { u32 op_recall; /* recall */ struct nfsd4_change_info op_cinfo; /* response */ u32 op_rflags; /* response */ - int op_truncate; /* used during processing */ + bool op_truncate; /* used during processing */ + bool op_created; /* used during processing */ struct nfs4_openowner *op_openowner; /* used during processing */ struct nfs4_file *op_file; /* used during processing */ struct nfs4_ol_stateid *op_stp; /* used during processing */ From a8d86cd75b709a9c9402c46674ea188493c53901 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 19 Oct 2011 11:42:03 -0400 Subject: [PATCH 097/103] nfsd4: remove unneeded CLAIM_DELEGATE_CUR workaround 0c12eaffdf09466f36a9ffe970dda8f4aeb6efc0 "nfsd: don't break lease on CLAIM_DELEGATE_CUR" was a temporary workaround for a problem fixed properly in the vfs layer by 778fc546f749c588aa2f6cd50215d2715c374252 "locks: fix tracking of inprogress lease breaks", so we can revert that change (but keeping some minor cleanup from that commit). Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3e1d4e08dfad..15e0db140403 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2653,12 +2653,6 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, int oflag = nfs4_access_to_omode(open->op_share_access); int access = nfs4_access_to_access(open->op_share_access); - /* CLAIM_DELEGATE_CUR is used in response to a broken lease; - * allowing it to break the lease and return EAGAIN leaves the - * client unable to make progress in returning the delegation */ - if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) - access |= NFSD_MAY_NOT_BREAK_LEASE; - if (!fp->fi_fds[oflag]) { status = nfsd_open(rqstp, cur_fh, S_IFREG, access, &fp->fi_fds[oflag]); From 8b289b2c2355c3bea75f3e499b4aa251a3191382 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 19 Oct 2011 11:52:12 -0400 Subject: [PATCH 098/103] nfsd4: implement new 4.1 open reclaim types Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 15 +++------------ fs/nfsd/nfs4state.c | 10 ++++++++-- fs/nfsd/nfs4xdr.c | 13 +++++++++++++ include/linux/nfs4.h | 5 ++++- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 710b97b7a2f3..458ebb6b59c7 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -366,12 +366,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, switch (open->op_claim_type) { case NFS4_OPEN_CLAIM_DELEGATE_CUR: case NFS4_OPEN_CLAIM_NULL: - /* - * (1) set CURRENT_FH to the file being opened, - * creating it if necessary, (2) set open->op_cinfo, - * (3) set open->op_truncate if the file is to be - * truncated after opening, (4) do permission checking. - */ status = do_open_lookup(rqstp, &cstate->current_fh, open); if (status) @@ -379,17 +373,14 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, break; case NFS4_OPEN_CLAIM_PREVIOUS: open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; - /* - * The CURRENT_FH is already set to the file being - * opened. (1) set open->op_cinfo, (2) set - * open->op_truncate if the file is to be truncated - * after opening, (3) do permission checking. - */ + case NFS4_OPEN_CLAIM_FH: + case NFS4_OPEN_CLAIM_DELEG_CUR_FH: status = do_open_fhandle(rqstp, &cstate->current_fh, open); if (status) goto out; break; + case NFS4_OPEN_CLAIM_DELEG_PREV_FH: case NFS4_OPEN_CLAIM_DELEGATE_PREV: open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; dprintk("NFSD: unsupported OPEN claim type %d\n", diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 15e0db140403..e8c2a3ec0e60 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2587,6 +2587,12 @@ static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, statei return delegstateid(ret); } +static bool nfsd4_is_deleg_cur(struct nfsd4_open *open) +{ + return open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR || + open->op_claim_type == NFS4_OPEN_CLAIM_DELEG_CUR_FH; +} + static __be32 nfs4_check_deleg(struct nfs4_client *cl, struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_delegation **dp) @@ -2602,7 +2608,7 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfs4_file *fp, struct nfsd4_open if (status) *dp = NULL; out: - if (open->op_claim_type != NFS4_OPEN_CLAIM_DELEGATE_CUR) + if (!nfsd4_is_deleg_cur(open)) return nfs_ok; if (status) return status; @@ -2879,7 +2885,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf goto out; } else { status = nfserr_bad_stateid; - if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) + if (nfsd4_is_deleg_cur(open)) goto out; status = nfserr_jukebox; fp = open->op_file; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 645a0a9d8073..fdc09a52cd8d 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -803,6 +803,19 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) return status; break; + case NFS4_OPEN_CLAIM_FH: + case NFS4_OPEN_CLAIM_DELEG_PREV_FH: + if (argp->minorversion < 1) + goto xdr_error; + /* void */ + break; + case NFS4_OPEN_CLAIM_DELEG_CUR_FH: + if (argp->minorversion < 1) + goto xdr_error; + status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid); + if (status) + return status; + break; default: goto xdr_error; } diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index b875b0324fc0..32345c2805c0 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -410,7 +410,10 @@ enum open_claim_type4 { NFS4_OPEN_CLAIM_NULL = 0, NFS4_OPEN_CLAIM_PREVIOUS = 1, NFS4_OPEN_CLAIM_DELEGATE_CUR = 2, - NFS4_OPEN_CLAIM_DELEGATE_PREV = 3 + NFS4_OPEN_CLAIM_DELEGATE_PREV = 3, + NFS4_OPEN_CLAIM_FH = 4, /* 4.1 */ + NFS4_OPEN_CLAIM_DELEG_CUR_FH = 5, /* 4.1 */ + NFS4_OPEN_CLAIM_DELEG_PREV_FH = 6, /* 4.1 */ }; enum opentype4 { From 5423732a71577f7860c56a4eea2c34ff162ddd73 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 19 Oct 2011 19:12:58 -0700 Subject: [PATCH 099/103] nfsd41: use SEQ4_STATUS_BACKCHANNEL_FAULT when cb_sequence is invalid Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 10 ++++++++++ fs/nfsd/nfs4state.c | 8 +++++++- fs/nfsd/state.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index de018ecadae6..7748d6a18d97 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -39,6 +39,8 @@ #define NFSDDBG_FACILITY NFSDDBG_PROC +static void nfsd4_mark_cb_fault(struct nfs4_client *, int reason); + #define NFSPROC4_CB_NULL 0 #define NFSPROC4_CB_COMPOUND 1 @@ -460,6 +462,8 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr, */ status = 0; out: + if (status) + nfsd4_mark_cb_fault(cb->cb_clp, status); return status; out_overflow: print_overflow_msg(__func__, xdr); @@ -686,6 +690,12 @@ static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason) warn_no_callback_path(clp, reason); } +static void nfsd4_mark_cb_fault(struct nfs4_client *clp, int reason) +{ + clp->cl_cb_state = NFSD4_CB_FAULT; + warn_no_callback_path(clp, reason); +} + static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata) { struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e8c2a3ec0e60..b51ad43b7ea1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1945,8 +1945,14 @@ out: nfsd4_get_session(cstate->session); atomic_inc(&clp->cl_refcount); - if (clp->cl_cb_state == NFSD4_CB_DOWN) + switch (clp->cl_cb_state) { + case NFSD4_CB_DOWN: seq->status_flags |= SEQ4_STATUS_CB_PATH_DOWN; + break; + case NFSD4_CB_FAULT: + seq->status_flags |= SEQ4_STATUS_BACKCHANNEL_FAULT; + break; + } } kfree(conn); spin_unlock(&client_lock); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 1a5820066040..a3cf38476a1b 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -258,6 +258,7 @@ struct nfs4_client { #define NFSD4_CB_UP 0 #define NFSD4_CB_UNKNOWN 1 #define NFSD4_CB_DOWN 2 +#define NFSD4_CB_FAULT 3 int cl_cb_state; struct nfsd4_callback cl_cb_null; struct nfsd4_session *cl_cb_session; From fc0c3dd13bac0675cdedc4e0d0641aa8a22e82de Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 19 Oct 2011 19:13:06 -0700 Subject: [PATCH 100/103] nfsd4: seq->status_flags may be used unitialized Reported-by: Gopala Suryanarayana Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index b51ad43b7ea1..1527aaffb000 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1947,11 +1947,13 @@ out: atomic_inc(&clp->cl_refcount); switch (clp->cl_cb_state) { case NFSD4_CB_DOWN: - seq->status_flags |= SEQ4_STATUS_CB_PATH_DOWN; + seq->status_flags = SEQ4_STATUS_CB_PATH_DOWN; break; case NFSD4_CB_FAULT: - seq->status_flags |= SEQ4_STATUS_BACKCHANNEL_FAULT; + seq->status_flags = SEQ4_STATUS_BACKCHANNEL_FAULT; break; + default: + seq->status_flags = 0; } } kfree(conn); From c668fc6dfcce98f8222ce1c997f4e5c4ac63f3d0 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 19 Oct 2011 19:13:13 -0700 Subject: [PATCH 101/103] nfsd4: allow NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL | NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED RFC5661 says: The client may set one or both of OPEN4_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL and OPEN4_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED. Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index fdc09a52cd8d..706ada1956c1 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -677,6 +677,8 @@ static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *x) switch (w) { case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL: case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED: + case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL | + NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED): return nfs_ok; } xdr_error: From 92bac8c5d60623167c6802b1f125e6d623708185 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 19 Oct 2011 19:13:29 -0700 Subject: [PATCH 102/103] nfsd4: typo logical vs bitwise negate for want_mask Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 706ada1956c1..decea140b323 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -671,7 +671,7 @@ static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *x) default: return nfserr_bad_xdr; } - w &= !NFS4_SHARE_WANT_MASK; + w &= ~NFS4_SHARE_WANT_MASK; if (!w) return nfs_ok; switch (w) { From 345c284290cabb5484df909303e73d6def8ec8ec Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Thu, 20 Oct 2011 17:51:39 +0800 Subject: [PATCH 103/103] nfs41: implement DESTROY_CLIENTID operation According to rfc5661 18.50, implement DESTROY_CLIENTID operation. Signed-off-by: Mi Jinlong Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 2 +- fs/nfsd/nfs4state.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ fs/nfsd/nfs4xdr.c | 12 +++++++++++- fs/nfsd/xdr4.h | 5 +++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 458ebb6b59c7..fa383361bc61 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1645,7 +1645,7 @@ static struct nfsd4_operation nfsd4_ops[] = { .op_name = "OP_SEQUENCE", }, [OP_DESTROY_CLIENTID] = { - .op_func = NULL, + .op_func = (nfsd4op_func)nfsd4_destroy_clientid, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP | OP_MODIFIES_SOMETHING, .op_name = "OP_DESTROY_CLIENTID", diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1527aaffb000..47e94e33a975 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1962,6 +1962,50 @@ out: return status; } +static inline bool has_resources(struct nfs4_client *clp) +{ + return !list_empty(&clp->cl_openowners) + || !list_empty(&clp->cl_delegations) + || !list_empty(&clp->cl_sessions); +} + +__be32 +nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_destroy_clientid *dc) +{ + struct nfs4_client *conf, *unconf, *clp; + int status = 0; + + nfs4_lock_state(); + unconf = find_unconfirmed_client(&dc->clientid); + conf = find_confirmed_client(&dc->clientid); + + if (conf) { + clp = conf; + + if (!is_client_expired(conf) && has_resources(conf)) { + status = nfserr_clientid_busy; + goto out; + } + + /* rfc5661 18.50.3 */ + if (cstate->session && conf == cstate->session->se_client) { + status = nfserr_clientid_busy; + goto out; + } + } else if (unconf) + clp = unconf; + else { + status = nfserr_stale_clientid; + goto out; + } + + expire_client(clp); +out: + nfs4_unlock_state(); + dprintk("%s return %d\n", __func__, ntohl(status)); + return status; +} + __be32 nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_reclaim_complete *rc) { diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index decea140b323..66d095d7955e 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1417,6 +1417,16 @@ xdr_error: goto out; } +static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp, struct nfsd4_destroy_clientid *dc) +{ + DECODE_HEAD; + + READ_BUF(8); + COPYMEM(&dc->clientid, 8); + + DECODE_TAIL; +} + static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc) { DECODE_HEAD; @@ -1538,7 +1548,7 @@ static nfsd4_dec nfsd41_dec_ops[] = { [OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_test_stateid, [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, - [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp, + [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_destroy_clientid, [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete, }; diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index e3057350eea1..2364747ee97d 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -398,6 +398,10 @@ struct nfsd4_destroy_session { struct nfs4_sessionid sessionid; }; +struct nfsd4_destroy_clientid { + clientid_t clientid; +}; + struct nfsd4_reclaim_complete { u32 rca_one_fs; }; @@ -552,6 +556,7 @@ extern __be32 nfsd4_sequence(struct svc_rqst *, extern __be32 nfsd4_destroy_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_destroy_session *); +extern __be32 nfsd4_destroy_clientid(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_destroy_clientid *); __be32 nfsd4_reclaim_complete(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_reclaim_complete *); extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, struct nfsd4_open *open);