@@ -790,12 +790,12 @@ nlmsvc_notify_blocked(struct file_lock *fl)
printk(KERN_WARNING "lockd: notification for unknown block!\n");
}
-static fl_owner_t nlmsvc_get_owner(fl_owner_t owner)
+static fl_owner_t nlmsvc_get_owner(fl_owner_t owner, bool lock)
{
return nlmsvc_get_lockowner(owner);
}
-static void nlmsvc_put_owner(fl_owner_t owner)
+static void nlmsvc_put_owner(fl_owner_t owner, bool unlock)
{
nlmsvc_put_lockowner(owner);
}
@@ -292,7 +292,8 @@ void locks_release_private(struct file_lock *fl)
if (fl->fl_lmops) {
if (fl->fl_lmops->lm_put_owner) {
- fl->fl_lmops->lm_put_owner(fl->fl_owner);
+ fl->fl_lmops->lm_put_owner(fl->fl_owner,
+ fl->fl_type == F_UNLCK);
fl->fl_owner = NULL;
}
fl->fl_lmops = NULL;
@@ -358,7 +359,8 @@ EXPORT_SYMBOL(locks_init_lock);
/*
* Initialize a new lock from an existing file_lock structure.
*/
-void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
+static void locks_copy_conflock(struct file_lock *new, struct file_lock *fl,
+ bool lock)
{
new->fl_owner = fl->fl_owner;
new->fl_pid = fl->fl_pid;
@@ -372,17 +374,16 @@ void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
if (fl->fl_lmops) {
if (fl->fl_lmops->lm_get_owner)
- fl->fl_lmops->lm_get_owner(fl->fl_owner);
+ fl->fl_lmops->lm_get_owner(fl->fl_owner, lock);
}
}
-EXPORT_SYMBOL(locks_copy_conflock);
void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{
/* "new" must be a freshly-initialized lock */
WARN_ON_ONCE(new->fl_ops);
- locks_copy_conflock(new, fl);
+ locks_copy_conflock(new, fl, true);
new->fl_file = fl->fl_file;
new->fl_ops = fl->fl_ops;
@@ -926,7 +927,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
module_put(owner);
goto retry;
}
- locks_copy_conflock(fl, cfl);
+ locks_copy_conflock(fl, cfl, false);
goto out;
}
fl->fl_type = F_UNLCK;
@@ -1145,7 +1146,7 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
goto retry;
}
if (conflock)
- locks_copy_conflock(conflock, fl);
+ locks_copy_conflock(conflock, fl, true);
error = -EAGAIN;
if (!(request->fl_flags & FL_SLEEP))
goto out;
@@ -6735,21 +6735,26 @@ nfs4_transform_lock_offset(struct file_lock *lock)
}
static fl_owner_t
-nfsd4_lm_get_owner(fl_owner_t owner)
+nfsd4_lm_get_owner(fl_owner_t owner, bool lock)
{
struct nfs4_lockowner *lo = (struct nfs4_lockowner *)owner;
nfs4_get_stateowner(&lo->lo_owner);
+ if (lock)
+ atomic_inc(&lo->lo_lockcnt);
return owner;
}
static void
-nfsd4_lm_put_owner(fl_owner_t owner)
+nfsd4_lm_put_owner(fl_owner_t owner, bool unlock)
{
struct nfs4_lockowner *lo = (struct nfs4_lockowner *)owner;
- if (lo)
+ if (lo) {
+ if (unlock)
+ atomic_dec(&lo->lo_lockcnt);
nfs4_put_stateowner(&lo->lo_owner);
+ }
}
/* return pointer to struct nfs4_client if client is expirable */
@@ -6903,6 +6908,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
return NULL;
INIT_LIST_HEAD(&lo->lo_blocked);
INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
+ atomic_set(&lo->lo_lockcnt, 0);
lo->lo_owner.so_is_open_owner = 0;
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
lo->lo_owner.so_ops = &lockowner_ops;
@@ -505,6 +505,7 @@ struct nfs4_openowner {
struct nfs4_lockowner {
struct nfs4_stateowner lo_owner; /* must be first element */
struct list_head lo_blocked; /* blocked file_locks */
+ atomic_t lo_lockcnt; /* count of locks held */
};
static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
@@ -1030,8 +1030,8 @@ struct file_lock_operations {
struct lock_manager_operations {
void *lm_mod_owner;
- fl_owner_t (*lm_get_owner)(fl_owner_t);
- void (*lm_put_owner)(fl_owner_t);
+ fl_owner_t (*lm_get_owner)(fl_owner_t owner, bool lock);
+ void (*lm_put_owner)(fl_owner_t owner, bool unlock);
void (*lm_notify)(struct file_lock *); /* unblock callback */
int (*lm_grant)(struct file_lock *, int);
bool (*lm_break)(struct file_lock *);
@@ -1153,10 +1153,9 @@ void locks_free_lock(struct file_lock *fl);
extern void locks_init_lock(struct file_lock *);
extern struct file_lock * locks_alloc_lock(void);
extern void locks_copy_lock(struct file_lock *, struct file_lock *);
-extern void locks_copy_conflock(struct file_lock *, struct file_lock *);
extern void locks_remove_posix(struct file *, fl_owner_t);
extern void locks_remove_file(struct file *);
-extern void locks_release_private(struct file_lock *);
+extern void locks_release_private(struct file_lock *fl);
extern void posix_test_lock(struct file *, struct file_lock *);
extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *);
extern int locks_delete_block(struct file_lock *);
@@ -1225,11 +1224,6 @@ static inline void locks_init_lock(struct file_lock *fl)
return;
}
-static inline void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
-{
- return;
-}
-
static inline void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{
return;
To properly handle an NFSv4.0 RELEASE_LOCKOWNER operation, NFSD needs to know whether there are locks associated with a lockowner. The current mechanism for making this assessment is heavyweight, to say the least. Instead, NFSD could count the number of locks based on the number of calls to ->lm_get_owner() and ->lm_put_owner(). The number of ->lm_get_owner() and ->lm_put_owner() calls should always be 100% balanced once all the locks belonging to a specific lock owner are removed. However, NFSD would have to sort out two cases: + When fs/locks adds or removes a lock record, and + When fs/locks copies a conflicting lock The latter case should never adjust the "locks held" count. Add a boolean argument to both APIs so that NFSD knows exactly when to bump and decrement the lockowner's new "locks held" counter. Suggested-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- fs/lockd/svclock.c | 4 ++-- fs/locks.c | 15 ++++++++------- fs/nfsd/nfs4state.c | 12 +++++++++--- fs/nfsd/state.h | 1 + include/linux/fs.h | 12 +++--------- 5 files changed, 23 insertions(+), 21 deletions(-)