diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index d120ec3..35b97e1 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -409,13 +409,20 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, } ret = nlm_drop_reply; goto out; - } - - if (!wait) + } else if (block->b_flags & B_QUEUED_WAIT) { + /* Don't retry a deferred blocking request */ + dprintk("lockd: nlmsvc_lock deferred blocking block %p flags %d\n", + block, block->b_flags); + error = -EINPROGRESS; + if (block->b_flags & B_GOT_CALLBACK) + error = block->b_result; + } else { + if (!wait) + lock->fl.fl_flags &= ~FL_SLEEP; + error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); lock->fl.fl_flags &= ~FL_SLEEP; - error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); - lock->fl.fl_flags &= ~FL_SLEEP; - + } + dprintk("lockd: vfs_lock_file returned %d\n", error); switch(error) { case 0: @@ -425,8 +432,10 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ret = nlm_lck_denied; break; case -EINPROGRESS: - if (wait) + if (wait) { + block->b_flags |= B_QUEUED_WAIT; break; + } /* Filesystem lock operation is in progress Add it to the queue waiting for callback */ ret = nlmsvc_defer_lock_rqst(rqstp, block); @@ -622,9 +631,11 @@ nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf, int result) { block->b_flags |= B_GOT_CALLBACK; + /* NOTE: a callback result of EAGAIN or EINPROGRESS is invalid */ + block->b_result = result; if (result == 0) block->b_granted = 1; - else + else if (block->b_flags & B_QUEUED) block->b_flags |= B_TIMED_OUT; if (conf) { if (block->b_fl) @@ -648,10 +659,9 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf, rc = -ENOLCK; break; } - nlmsvc_update_deferred_block(block, conf, result); - } else if (result == 0) - block->b_granted = 1; - + } + nlmsvc_update_deferred_block(block, conf, result); + nlmsvc_insert_block(block, 0); svc_wake_up(block->b_daemon); rc = 0; @@ -732,11 +742,18 @@ nlmsvc_grant_blocked(struct nlm_block *block) goto callback; } - /* Try the lock operation again */ - lock->fl.fl_flags |= FL_SLEEP; - error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); - lock->fl.fl_flags &= ~FL_SLEEP; - + if (block->b_flags & B_QUEUED_WAIT) { + /* Don't retry a deferred blocking request */ + error = -EINPROGRESS; + if (block->b_flags & B_GOT_CALLBACK) + error = block->b_result; + } else { + /* Try the lock operation again */ + lock->fl.fl_flags |= FL_SLEEP; + error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); + lock->fl.fl_flags &= ~FL_SLEEP; + } + switch (error) { case 0: break; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index e2d1ce3..187afcf 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -137,10 +137,12 @@ struct nlm_block { struct cache_req * b_cache_req; /* deferred request handling */ struct file_lock * b_fl; /* set for GETLK */ struct cache_deferred_req * b_deferred_req; + unsigned int b_result; /* callback result */ unsigned int b_flags; /* block flags */ #define B_QUEUED 1 /* lock queued */ #define B_GOT_CALLBACK 2 /* got lock or conflicting lock */ #define B_TIMED_OUT 4 /* filesystem too slow to respond */ +#define B_QUEUED_WAIT 8 /* blocking lock queued */ }; /*