Merge branch 'server-cluster-locking-api' of git://linux-nfs.org/~bfields/linux
* 'server-cluster-locking-api' of git://linux-nfs.org/~bfields/linux: gfs2: nfs lock support for gfs2 lockd: add code to handle deferred lock requests lockd: always preallocate block in nlmsvc_lock() lockd: handle test_lock deferrals lockd: pass cookie in nlmsvc_testlock lockd: handle fl_grant callbacks lockd: save lock state on deferral locks: add fl_grant callback for asynchronous lock return nfsd4: Convert NFSv4 to new lock interface locks: add lock cancel command locks: allow {vfs,posix}_lock_file to return conflicting lock locks: factor out generic/filesystem switch from setlock code locks: factor out generic/filesystem switch from test_lock locks: give posix_test_lock same interface as ->lock locks: make ->lock release private data before returning in GETLK case locks: create posix-to-flock helper functions locks: trivial removal of unnecessary parentheses
This commit is contained in:
264
fs/locks.c
264
fs/locks.c
@@ -665,11 +665,11 @@ static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *w
|
||||
}
|
||||
|
||||
int
|
||||
posix_test_lock(struct file *filp, struct file_lock *fl,
|
||||
struct file_lock *conflock)
|
||||
posix_test_lock(struct file *filp, struct file_lock *fl)
|
||||
{
|
||||
struct file_lock *cfl;
|
||||
|
||||
fl->fl_type = F_UNLCK;
|
||||
lock_kernel();
|
||||
for (cfl = filp->f_path.dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
|
||||
if (!IS_POSIX(cfl))
|
||||
@@ -678,7 +678,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl,
|
||||
break;
|
||||
}
|
||||
if (cfl) {
|
||||
__locks_copy_lock(conflock, cfl);
|
||||
__locks_copy_lock(fl, cfl);
|
||||
unlock_kernel();
|
||||
return 1;
|
||||
}
|
||||
@@ -800,7 +800,7 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request, struct file_lock *conflock)
|
||||
static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock)
|
||||
{
|
||||
struct file_lock *fl;
|
||||
struct file_lock *new_fl = NULL;
|
||||
@@ -1006,6 +1006,7 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request
|
||||
* posix_lock_file - Apply a POSIX-style lock to a file
|
||||
* @filp: The file to apply the lock to
|
||||
* @fl: The lock to be applied
|
||||
* @conflock: Place to return a copy of the conflicting lock, if found.
|
||||
*
|
||||
* Add a POSIX style lock to a file.
|
||||
* We merge adjacent & overlapping locks whenever possible.
|
||||
@@ -1015,26 +1016,12 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request
|
||||
* whether or not a lock was successfully freed by testing the return
|
||||
* value for -ENOENT.
|
||||
*/
|
||||
int posix_lock_file(struct file *filp, struct file_lock *fl)
|
||||
{
|
||||
return __posix_lock_file_conf(filp->f_path.dentry->d_inode, fl, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(posix_lock_file);
|
||||
|
||||
/**
|
||||
* posix_lock_file_conf - Apply a POSIX-style lock to a file
|
||||
* @filp: The file to apply the lock to
|
||||
* @fl: The lock to be applied
|
||||
* @conflock: Place to return a copy of the conflicting lock, if found.
|
||||
*
|
||||
* Except for the conflock parameter, acts just like posix_lock_file.
|
||||
*/
|
||||
int posix_lock_file_conf(struct file *filp, struct file_lock *fl,
|
||||
int posix_lock_file(struct file *filp, struct file_lock *fl,
|
||||
struct file_lock *conflock)
|
||||
{
|
||||
return __posix_lock_file_conf(filp->f_path.dentry->d_inode, fl, conflock);
|
||||
return __posix_lock_file(filp->f_path.dentry->d_inode, fl, conflock);
|
||||
}
|
||||
EXPORT_SYMBOL(posix_lock_file_conf);
|
||||
EXPORT_SYMBOL(posix_lock_file);
|
||||
|
||||
/**
|
||||
* posix_lock_file_wait - Apply a POSIX-style lock to a file
|
||||
@@ -1050,7 +1037,7 @@ int posix_lock_file_wait(struct file *filp, struct file_lock *fl)
|
||||
int error;
|
||||
might_sleep ();
|
||||
for (;;) {
|
||||
error = posix_lock_file(filp, fl);
|
||||
error = posix_lock_file(filp, fl, NULL);
|
||||
if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP))
|
||||
break;
|
||||
error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
|
||||
@@ -1122,7 +1109,7 @@ int locks_mandatory_area(int read_write, struct inode *inode,
|
||||
fl.fl_end = offset + count - 1;
|
||||
|
||||
for (;;) {
|
||||
error = __posix_lock_file_conf(inode, &fl, NULL);
|
||||
error = __posix_lock_file(inode, &fl, NULL);
|
||||
if (error != -EAGAIN)
|
||||
break;
|
||||
if (!(fl.fl_flags & FL_SLEEP))
|
||||
@@ -1610,12 +1597,62 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* vfs_test_lock - test file byte range lock
|
||||
* @filp: The file to test lock for
|
||||
* @fl: The lock to test
|
||||
* @conf: Place to return a copy of the conflicting lock, if found
|
||||
*
|
||||
* Returns -ERRNO on failure. Indicates presence of conflicting lock by
|
||||
* setting conf->fl_type to something other than F_UNLCK.
|
||||
*/
|
||||
int vfs_test_lock(struct file *filp, struct file_lock *fl)
|
||||
{
|
||||
if (filp->f_op && filp->f_op->lock)
|
||||
return filp->f_op->lock(filp, F_GETLK, fl);
|
||||
posix_test_lock(filp, fl);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfs_test_lock);
|
||||
|
||||
static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)
|
||||
{
|
||||
flock->l_pid = fl->fl_pid;
|
||||
#if BITS_PER_LONG == 32
|
||||
/*
|
||||
* Make sure we can represent the posix lock via
|
||||
* legacy 32bit flock.
|
||||
*/
|
||||
if (fl->fl_start > OFFT_OFFSET_MAX)
|
||||
return -EOVERFLOW;
|
||||
if (fl->fl_end != OFFSET_MAX && fl->fl_end > OFFT_OFFSET_MAX)
|
||||
return -EOVERFLOW;
|
||||
#endif
|
||||
flock->l_start = fl->fl_start;
|
||||
flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
|
||||
fl->fl_end - fl->fl_start + 1;
|
||||
flock->l_whence = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if BITS_PER_LONG == 32
|
||||
static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
|
||||
{
|
||||
flock->l_pid = fl->fl_pid;
|
||||
flock->l_start = fl->fl_start;
|
||||
flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
|
||||
fl->fl_end - fl->fl_start + 1;
|
||||
flock->l_whence = 0;
|
||||
flock->l_type = fl->fl_type;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Report the first existing lock that would conflict with l.
|
||||
* This implements the F_GETLK command of fcntl().
|
||||
*/
|
||||
int fcntl_getlk(struct file *filp, struct flock __user *l)
|
||||
{
|
||||
struct file_lock *fl, cfl, file_lock;
|
||||
struct file_lock file_lock;
|
||||
struct flock flock;
|
||||
int error;
|
||||
|
||||
@@ -1630,38 +1667,15 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (filp->f_op && filp->f_op->lock) {
|
||||
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
|
||||
if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
|
||||
file_lock.fl_ops->fl_release_private(&file_lock);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
else
|
||||
fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
|
||||
} else {
|
||||
fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL);
|
||||
}
|
||||
error = vfs_test_lock(filp, &file_lock);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
flock.l_type = F_UNLCK;
|
||||
if (fl != NULL) {
|
||||
flock.l_pid = fl->fl_pid;
|
||||
#if BITS_PER_LONG == 32
|
||||
/*
|
||||
* Make sure we can represent the posix lock via
|
||||
* legacy 32bit flock.
|
||||
*/
|
||||
error = -EOVERFLOW;
|
||||
if (fl->fl_start > OFFT_OFFSET_MAX)
|
||||
flock.l_type = file_lock.fl_type;
|
||||
if (file_lock.fl_type != F_UNLCK) {
|
||||
error = posix_lock_to_flock(&flock, &file_lock);
|
||||
if (error)
|
||||
goto out;
|
||||
if ((fl->fl_end != OFFSET_MAX)
|
||||
&& (fl->fl_end > OFFT_OFFSET_MAX))
|
||||
goto out;
|
||||
#endif
|
||||
flock.l_start = fl->fl_start;
|
||||
flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
|
||||
fl->fl_end - fl->fl_start + 1;
|
||||
flock.l_whence = 0;
|
||||
flock.l_type = fl->fl_type;
|
||||
}
|
||||
error = -EFAULT;
|
||||
if (!copy_to_user(l, &flock, sizeof(flock)))
|
||||
@@ -1670,6 +1684,48 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* vfs_lock_file - file byte range lock
|
||||
* @filp: The file to apply the lock to
|
||||
* @cmd: type of locking operation (F_SETLK, F_GETLK, etc.)
|
||||
* @fl: The lock to be applied
|
||||
* @conf: Place to return a copy of the conflicting lock, if found.
|
||||
*
|
||||
* A caller that doesn't care about the conflicting lock may pass NULL
|
||||
* as the final argument.
|
||||
*
|
||||
* If the filesystem defines a private ->lock() method, then @conf will
|
||||
* be left unchanged; so a caller that cares should initialize it to
|
||||
* some acceptable default.
|
||||
*
|
||||
* To avoid blocking kernel daemons, such as lockd, that need to acquire POSIX
|
||||
* locks, the ->lock() interface may return asynchronously, before the lock has
|
||||
* been granted or denied by the underlying filesystem, if (and only if)
|
||||
* fl_grant is set. Callers expecting ->lock() to return asynchronously
|
||||
* will only use F_SETLK, not F_SETLKW; they will set FL_SLEEP if (and only if)
|
||||
* the request is for a blocking lock. When ->lock() does return asynchronously,
|
||||
* it must return -EINPROGRESS, and call ->fl_grant() when the lock
|
||||
* request completes.
|
||||
* If the request is for non-blocking lock the file system should return
|
||||
* -EINPROGRESS then try to get the lock and call the callback routine with
|
||||
* the result. If the request timed out the callback routine will return a
|
||||
* nonzero return code and the file system should release the lock. The file
|
||||
* system is also responsible to keep a corresponding posix lock when it
|
||||
* grants a lock so the VFS can find out which locks are locally held and do
|
||||
* the correct lock cleanup when required.
|
||||
* The underlying filesystem must not drop the kernel lock or call
|
||||
* ->fl_grant() before returning to the caller with a -EINPROGRESS
|
||||
* return code.
|
||||
*/
|
||||
int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf)
|
||||
{
|
||||
if (filp->f_op && filp->f_op->lock)
|
||||
return filp->f_op->lock(filp, cmd, fl);
|
||||
else
|
||||
return posix_lock_file(filp, fl, conf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfs_lock_file);
|
||||
|
||||
/* Apply the lock described by l to an open file descriptor.
|
||||
* This implements both the F_SETLK and F_SETLKW commands of fcntl().
|
||||
*/
|
||||
@@ -1732,21 +1788,17 @@ again:
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (filp->f_op && filp->f_op->lock != NULL)
|
||||
error = filp->f_op->lock(filp, cmd, file_lock);
|
||||
else {
|
||||
for (;;) {
|
||||
error = posix_lock_file(filp, file_lock);
|
||||
if ((error != -EAGAIN) || (cmd == F_SETLK))
|
||||
break;
|
||||
error = wait_event_interruptible(file_lock->fl_wait,
|
||||
!file_lock->fl_next);
|
||||
if (!error)
|
||||
continue;
|
||||
|
||||
locks_delete_block(file_lock);
|
||||
for (;;) {
|
||||
error = vfs_lock_file(filp, cmd, file_lock, NULL);
|
||||
if (error != -EAGAIN || cmd == F_SETLK)
|
||||
break;
|
||||
}
|
||||
error = wait_event_interruptible(file_lock->fl_wait,
|
||||
!file_lock->fl_next);
|
||||
if (!error)
|
||||
continue;
|
||||
|
||||
locks_delete_block(file_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1769,7 +1821,7 @@ out:
|
||||
*/
|
||||
int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
|
||||
{
|
||||
struct file_lock *fl, cfl, file_lock;
|
||||
struct file_lock file_lock;
|
||||
struct flock64 flock;
|
||||
int error;
|
||||
|
||||
@@ -1784,27 +1836,14 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (filp->f_op && filp->f_op->lock) {
|
||||
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
|
||||
if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
|
||||
file_lock.fl_ops->fl_release_private(&file_lock);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
else
|
||||
fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
|
||||
} else {
|
||||
fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL);
|
||||
}
|
||||
|
||||
flock.l_type = F_UNLCK;
|
||||
if (fl != NULL) {
|
||||
flock.l_pid = fl->fl_pid;
|
||||
flock.l_start = fl->fl_start;
|
||||
flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
|
||||
fl->fl_end - fl->fl_start + 1;
|
||||
flock.l_whence = 0;
|
||||
flock.l_type = fl->fl_type;
|
||||
}
|
||||
error = vfs_test_lock(filp, &file_lock);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
flock.l_type = file_lock.fl_type;
|
||||
if (file_lock.fl_type != F_UNLCK)
|
||||
posix_lock_to_flock64(&flock, &file_lock);
|
||||
|
||||
error = -EFAULT;
|
||||
if (!copy_to_user(l, &flock, sizeof(flock)))
|
||||
error = 0;
|
||||
@@ -1875,21 +1914,17 @@ again:
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (filp->f_op && filp->f_op->lock != NULL)
|
||||
error = filp->f_op->lock(filp, cmd, file_lock);
|
||||
else {
|
||||
for (;;) {
|
||||
error = posix_lock_file(filp, file_lock);
|
||||
if ((error != -EAGAIN) || (cmd == F_SETLK64))
|
||||
break;
|
||||
error = wait_event_interruptible(file_lock->fl_wait,
|
||||
!file_lock->fl_next);
|
||||
if (!error)
|
||||
continue;
|
||||
|
||||
locks_delete_block(file_lock);
|
||||
for (;;) {
|
||||
error = vfs_lock_file(filp, cmd, file_lock, NULL);
|
||||
if (error != -EAGAIN || cmd == F_SETLK64)
|
||||
break;
|
||||
}
|
||||
error = wait_event_interruptible(file_lock->fl_wait,
|
||||
!file_lock->fl_next);
|
||||
if (!error)
|
||||
continue;
|
||||
|
||||
locks_delete_block(file_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1934,10 +1969,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner)
|
||||
lock.fl_ops = NULL;
|
||||
lock.fl_lmops = NULL;
|
||||
|
||||
if (filp->f_op && filp->f_op->lock != NULL)
|
||||
filp->f_op->lock(filp, F_SETLK, &lock);
|
||||
else
|
||||
posix_lock_file(filp, &lock);
|
||||
vfs_lock_file(filp, F_SETLK, &lock, NULL);
|
||||
|
||||
if (lock.fl_ops && lock.fl_ops->fl_release_private)
|
||||
lock.fl_ops->fl_release_private(&lock);
|
||||
@@ -2014,6 +2046,22 @@ posix_unblock_lock(struct file *filp, struct file_lock *waiter)
|
||||
|
||||
EXPORT_SYMBOL(posix_unblock_lock);
|
||||
|
||||
/**
|
||||
* vfs_cancel_lock - file byte range unblock lock
|
||||
* @filp: The file to apply the unblock to
|
||||
* @fl: The lock to be unblocked
|
||||
*
|
||||
* Used by lock managers to cancel blocked requests
|
||||
*/
|
||||
int vfs_cancel_lock(struct file *filp, struct file_lock *fl)
|
||||
{
|
||||
if (filp->f_op && filp->f_op->lock)
|
||||
return filp->f_op->lock(filp, F_CANCELLK, fl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(vfs_cancel_lock);
|
||||
|
||||
static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)
|
||||
{
|
||||
struct inode *inode = NULL;
|
||||
|
Reference in New Issue
Block a user