diff -urpNX dontdiff linux-2.5.52/fs/lockd/clntproc.c linux-2.5.52-flock/fs/lockd/clntproc.c
--- linux-2.5.52/fs/lockd/clntproc.c 2002-11-17 23:29:21.000000000 -0500
+++ linux-2.5.52-flock/fs/lockd/clntproc.c 2002-12-18 14:03:18.000000000 -0500
@@ -401,12 +401,11 @@ nlmclnt_test(struct nlm_rqst *req, struc
return 0;
}
-static
void nlmclnt_insert_lock_callback(struct file_lock *fl)
{
nlm_get_host(fl->fl_u.nfs_fl.host);
}
-static
+
void nlmclnt_remove_lock_callback(struct file_lock *fl)
{
if (fl->fl_u.nfs_fl.host) {
@@ -462,8 +461,6 @@ nlmclnt_lock(struct nlm_rqst *req, struc
fl->fl_u.nfs_fl.state = host->h_state;
fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
fl->fl_u.nfs_fl.host = host;
- fl->fl_insert = nlmclnt_insert_lock_callback;
- fl->fl_remove = nlmclnt_remove_lock_callback;
}
return nlm_stat_to_errno(resp->status);
diff -urpNX dontdiff linux-2.5.52/fs/lockd/svclock.c linux-2.5.52-flock/fs/lockd/svclock.c
--- linux-2.5.52/fs/lockd/svclock.c 2002-11-17 23:29:29.000000000 -0500
+++ linux-2.5.52-flock/fs/lockd/svclock.c 2002-12-18 14:23:35.000000000 -0500
@@ -293,7 +293,6 @@ u32
nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
{
- struct file_lock *conflock;
struct nlm_block *block;
int error;
@@ -312,59 +311,57 @@ nlmsvc_lock(struct svc_rqst *rqstp, stru
block = nlmsvc_lookup_block(file, lock, 0);
lock->fl.fl_flags |= FL_LOCKD;
+ if (wait)
+ lock->fl.fl_flags |= FL_SLEEP;
-again:
- if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) {
- error = posix_lock_file(&file->f_file, &lock->fl);
+ error = vfs_setlock_posix(&file->f_file, &lock->fl);
+ dprintk("lockd: vfs_setlock_posix returned %d\n", -error);
+ if ((error != -EAGAIN) || !wait)
+ goto out;
- if (block)
- nlmsvc_delete_block(block, 0);
- up(&file->f_sema);
-
- dprintk("lockd: posix_lock_file returned %d\n", -error);
- switch(-error) {
- case 0:
- return nlm_granted;
- case EDEADLK:
- return nlm_deadlock;
- case EAGAIN:
- return nlm_lck_denied;
- default: /* includes ENOLCK */
- return nlm_lck_denied_nolocks;
+ /* If we don't have a block, create and initialize it. */
+ if (!block) {
+ dprintk("lockd: blocking on this lock (allocating).\n");
+ block = nlmsvc_create_block(rqstp, file, lock, cookie);
+ if (!block) {
+ error = -ENOLCK;
+ goto out;
}
}
- if (!wait) {
- up(&file->f_sema);
- return nlm_lck_denied;
- }
+ /* Append to list of blocked locks */
+ nlmsvc_insert_block(block, NLM_NEVER);
- if (posix_locks_deadlock(&lock->fl, conflock)) {
- up(&file->f_sema);
- return nlm_deadlock;
+ /*
+ * There's a race a mile wide there. vfs_setlock_posix() adds us
+ * to the blocked list, but we may have just slept to allocate
+ * memory. So check to see whether we're still blocked and issue
+ * a wakeup if we're not.
+ */
+ if (!lock->fl.fl_next) {
+ nlmsvc_insert_block(block, 0);
+ svc_wake_up(block->b_daemon);
}
+ block = NULL;
- /* If we don't have a block, create and initialize it. Then
- * retry because we may have slept in kmalloc. */
- if (block == NULL) {
- dprintk("lockd: blocking on this lock (allocating).\n");
- if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie)))
- return nlm_lck_denied_nolocks;
- goto again;
- }
+ out:
+ up(&file->f_sema);
- /* Append to list of blocked */
- nlmsvc_insert_block(block, NLM_NEVER);
+ if (block)
+ nlmsvc_delete_block(block, 0);
- if (list_empty(&block->b_call.a_args.lock.fl.fl_block)) {
- /* Now add block to block list of the conflicting lock
- if we haven't done so. */
- dprintk("lockd: blocking on this lock.\n");
- posix_block_lock(conflock, &block->b_call.a_args.lock.fl);
+ if (!error) {
+ return nlm_granted;
+ } else if (error == -EDEADLK) {
+ return nlm_deadlock;
+ } else if (error == -EAGAIN) {
+ if (wait)
+ return nlm_lck_blocked;
+ else
+ return nlm_lck_denied;
+ } else {
+ return nlm_lck_denied_nolocks;
}
-
- up(&file->f_sema);
- return nlm_lck_blocked;
}
/*
@@ -374,8 +371,6 @@ u32
nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
struct nlm_lock *conflock)
{
- struct file_lock *fl;
-
dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
file->f_file.f_dentry->d_inode->i_sb->s_id,
file->f_file.f_dentry->d_inode->i_ino,
@@ -383,13 +378,15 @@ nlmsvc_testlock(struct nlm_file *file, s
(long long)lock->fl.fl_start,
(long long)lock->fl.fl_end);
- if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
+ conflock->fl = lock->fl; /* a struct copy */
+ vfs_testlock_posix(&file->f_file, &conflock->fl);
+ if (conflock->fl.fl_type != F_UNLCK) {
dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
- fl->fl_type, (long long)fl->fl_start,
- (long long)fl->fl_end);
+ conflock->fl.fl_type,
+ (long long)conflock->fl.fl_start,
+ (long long)conflock->fl.fl_end);
conflock->caller = "somehost"; /* FIXME */
conflock->oh.len = 0; /* don't return OH info */
- conflock->fl = *fl;
return nlm_lck_denied;
}
@@ -419,7 +416,7 @@ nlmsvc_unlock(struct nlm_file *file, str
nlmsvc_cancel_blocked(file, lock);
lock->fl.fl_type = F_UNLCK;
- error = posix_lock_file(&file->f_file, &lock->fl);
+ error = vfs_setlock_posix(&file->f_file, &lock->fl);
return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
}
@@ -490,7 +487,6 @@ nlmsvc_grant_blocked(struct nlm_block *b
{
struct nlm_file *file = block->b_file;
struct nlm_lock *lock = &block->b_call.a_args.lock;
- struct file_lock *conflock;
int error;
dprintk("lockd: grant blocked lock %p\n", block);
@@ -506,32 +502,13 @@ nlmsvc_grant_blocked(struct nlm_block *b
* binding */
if (block->b_granted) {
nlm_rebind_host(block->b_host);
- goto callback;
- }
-
- /* Try the lock operation again */
- if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
- /* Bummer, we blocked again */
- dprintk("lockd: lock still blocked\n");
- nlmsvc_insert_block(block, NLM_NEVER);
- posix_block_lock(conflock, &lock->fl);
- up(&file->f_sema);
- return;
- }
-
- /* Alright, no conflicting lock. Now lock it for real. If the
- * following yields an error, this is most probably due to low
- * memory. Retry the lock in a few seconds.
- */
- if ((error = posix_lock_file(&file->f_file, &lock->fl)) < 0) {
- printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
- -error, __FUNCTION__);
- nlmsvc_insert_block(block, 10 * HZ);
- up(&file->f_sema);
- return;
+ } else {
+ /* Try the lock operation again */
+ error = vfs_setlock_posix(&file->f_file, &lock->fl);
+ if (error)
+ goto err;
}
-callback:
/* Lock was granted by VFS. */
dprintk("lockd: GRANTing blocked lock.\n");
block->b_granted = 1;
@@ -546,6 +523,18 @@ callback:
nlmsvc_grant_callback) < 0)
nlm_release_host(block->b_call.a_host);
up(&file->f_sema);
+ return;
+
+ err:
+ dprintk("lockd: blocked lock retry returned %d\n", error);
+ if (error == -EAGAIN) {
+ /* We actually blocked, so go back to sleep */
+ nlmsvc_insert_block(block, NLM_NEVER);
+ } else {
+ /* Some other error. We must retry later. */
+ nlmsvc_insert_block(block, 10 * HZ);
+ }
+ up(&file->f_sema);
}
/*
diff -urpNX dontdiff linux-2.5.52/fs/lockd/svcsubs.c linux-2.5.52-flock/fs/lockd/svcsubs.c
--- linux-2.5.52/fs/lockd/svcsubs.c 2002-11-17 23:29:48.000000000 -0500
+++ linux-2.5.52-flock/fs/lockd/svcsubs.c 2002-12-18 14:03:18.000000000 -0500
@@ -176,7 +176,7 @@ again:
lock.fl_type = F_UNLCK;
lock.fl_start = 0;
lock.fl_end = OFFSET_MAX;
- if (posix_lock_file(&file->f_file, &lock) < 0) {
+ if (vfs_setlock_posix(&file->f_file, &lock) < 0) {
printk("lockd: unlock failure in %s:%d\n",
__FILE__, __LINE__);
return 1;
diff -urpNX dontdiff linux-2.5.52/fs/locks.c linux-2.5.52-flock/fs/locks.c
--- linux-2.5.52/fs/locks.c 2002-12-18 14:00:37.000000000 -0500
+++ linux-2.5.52-flock/fs/locks.c 2002-12-18 14:09:44.000000000 -0500
@@ -114,17 +114,18 @@
* Stephen Rothwell <
[email protected]>, June, 2000.
*/
-#include <linux/slab.h>
-#include <linux/file.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
#include <linux/capability.h>
-#include <linux/timer.h>
-#include <linux/time.h>
+#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/sched.h>
#include <linux/security.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/wait.h>
-#include <asm/semaphore.h>
#include <asm/uaccess.h>
#define IS_POSIX(fl) (fl->fl_flags & FL_POSIX)
@@ -188,8 +189,6 @@ void locks_init_lock(struct file_lock *f
fl->fl_type = 0;
fl->fl_start = fl->fl_end = 0;
fl->fl_notify = NULL;
- fl->fl_insert = NULL;
- fl->fl_remove = NULL;
}
/*
@@ -220,8 +219,6 @@ void locks_copy_lock(struct file_lock *n
new->fl_start = fl->fl_start;
new->fl_end = fl->fl_end;
new->fl_notify = fl->fl_notify;
- new->fl_insert = fl->fl_insert;
- new->fl_remove = fl->fl_remove;
new->fl_u = fl->fl_u;
}
@@ -322,8 +319,6 @@ static int flock_to_posix_lock(struct fi
fl->fl_file = filp;
fl->fl_flags = FL_POSIX;
fl->fl_notify = NULL;
- fl->fl_insert = NULL;
- fl->fl_remove = NULL;
return assign_type(fl, l->l_type);
}
@@ -362,8 +357,6 @@ static int flock64_to_posix_lock(struct
fl->fl_file = filp;
fl->fl_flags = FL_POSIX;
fl->fl_notify = NULL;
- fl->fl_insert = NULL;
- fl->fl_remove = NULL;
switch (l->l_type) {
case F_RDLCK:
@@ -398,8 +391,6 @@ static int lease_alloc(struct file *filp
fl->fl_start = 0;
fl->fl_end = OFFSET_MAX;
fl->fl_notify = NULL;
- fl->fl_insert = NULL;
- fl->fl_remove = NULL;
*flp = fl;
return 0;
@@ -475,14 +466,17 @@ static void locks_wake_up_blocks(struct
*/
static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
{
+ struct lock_operations *l_op = NULL;
list_add(&fl->fl_link, &file_lock_list);
/* insert into file's list */
fl->fl_next = *pos;
*pos = fl;
- if (fl->fl_insert)
- fl->fl_insert(fl);
+ if (fl->fl_file->f_op)
+ l_op = fl->fl_file->f_op->lock;
+ if (l_op && l_op->lock_insert)
+ l_op->lock_insert(fl);
}
/*
@@ -494,6 +488,7 @@ static void locks_insert_lock(struct fil
static void locks_delete_lock(struct file_lock **thisfl_p)
{
struct file_lock *fl = *thisfl_p;
+ struct lock_operations *l_op = NULL;
*thisfl_p = fl->fl_next;
fl->fl_next = NULL;
@@ -505,8 +500,10 @@ static void locks_delete_lock(struct fil
fl->fl_fasync = NULL;
}
- if (fl->fl_remove)
- fl->fl_remove(fl);
+ if (fl->fl_file->f_op)
+ l_op = fl->fl_file->f_op->lock;
+ if (l_op && l_op->lock_remove)
+ l_op->lock_remove(fl);
locks_wake_up_blocks(fl);
locks_free_lock(fl);
@@ -593,8 +590,15 @@ static int locks_block_on_timeout(struct
return result;
}
-struct file_lock *
-posix_test_lock(struct file *filp, struct file_lock *fl)
+/**
+ * posix_test_lock - finds a conflicting lock
+ * @filp: the file to search
+ * @fl: the template to check
+ *
+ * This function overwrites @fl with any lock which overlaps it and is
+ * owned by a different task.
+ */
+static void posix_test_lock(struct file *filp, struct file_lock *fl)
{
struct file_lock *cfl;
@@ -602,12 +606,13 @@ posix_test_lock(struct file *filp, struc
for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
if (!IS_POSIX(cfl))
continue;
- if (posix_locks_conflict(cfl, fl))
- break;
+ if (!posix_locks_conflict(cfl, fl))
+ continue;
+
+ *fl = *cfl;
+ break;
}
unlock_kernel();
-
- return (cfl);
}
/* This function tests for deadlock condition before putting a process to
@@ -624,7 +629,7 @@ posix_test_lock(struct file *filp, struc
* from a broken NFS client. But broken NFS clients have a lot more to
* worry about than proper deadlock detection anyway... --okir
*/
-int posix_locks_deadlock(struct file_lock *caller_fl,
+static int posix_locks_deadlock(struct file_lock *caller_fl,
struct file_lock *block_fl)
{
struct list_head *tmp;
@@ -652,63 +657,6 @@ next_task:
return 0;
}
-int locks_mandatory_locked(struct inode *inode)
-{
- fl_owner_t owner = current->files;
- struct file_lock *fl;
-
- /*
- * Search the lock list for this inode for any POSIX locks.
- */
- lock_kernel();
- for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (!IS_POSIX(fl))
- continue;
- if (fl->fl_owner != owner)
- break;
- }
- unlock_kernel();
- return fl ? -EAGAIN : 0;
-}
-
-int locks_mandatory_area(int read_write, struct inode *inode,
- struct file *filp, loff_t offset,
- size_t count)
-{
- struct file_lock fl;
- int error;
-
- fl.fl_owner = current->files;
- fl.fl_pid = current->tgid;
- fl.fl_file = filp;
- fl.fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP;
- fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
- fl.fl_start = offset;
- fl.fl_end = offset + count - 1;
-
- for (;;) {
- error = posix_lock_file(filp, &fl);
- if (error != -EAGAIN)
- break;
- error = wait_event_interruptible(fl.fl_wait, !fl.fl_next);
- if (!error) {
- /*
- * If we've been sleeping someone might have
- * changed the permissions behind our back.
- */
- if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
- continue;
- }
-
- lock_kernel();
- locks_delete_block(&fl);
- unlock_kernel();
- break;
- }
-
- return error;
-}
-
/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
* at the head of the list, but that's secret knowledge known only to
* flock_lock_file and posix_lock_file.
@@ -788,7 +736,7 @@ out:
* To all purists: Yes, I use a few goto's. Just pass on to the next function.
*/
-int posix_lock_file(struct file *filp, struct file_lock *request)
+static int posix_lock_file(struct file *filp, struct file_lock *request)
{
struct file_lock *fl;
struct file_lock *new_fl, *new_fl2;
@@ -1103,6 +1051,63 @@ out:
return error;
}
+int locks_mandatory_locked(struct inode *inode)
+{
+ fl_owner_t owner = current->files;
+ struct file_lock *fl;
+
+ /*
+ * Search the lock list for this inode for any POSIX locks.
+ */
+ lock_kernel();
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (!IS_POSIX(fl))
+ continue;
+ if (fl->fl_owner != owner)
+ break;
+ }
+ unlock_kernel();
+ return fl ? -EAGAIN : 0;
+}
+
+int locks_mandatory_area(int read_write, struct inode *inode,
+ struct file *filp, loff_t offset,
+ size_t count)
+{
+ struct file_lock fl;
+ int error;
+
+ fl.fl_owner = current->files;
+ fl.fl_pid = current->tgid;
+ fl.fl_file = filp;
+ fl.fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP;
+ fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
+ fl.fl_start = offset;
+ fl.fl_end = offset + count - 1;
+
+ for (;;) {
+ error = posix_lock_file(filp, &fl);
+ if (error != -EAGAIN)
+ break;
+ error = wait_event_interruptible(fl.fl_wait, !fl.fl_next);
+ if (!error) {
+ /*
+ * If we've been sleeping someone might have
+ * changed the permissions behind our back.
+ */
+ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+ continue;
+ }
+
+ lock_kernel();
+ locks_delete_block(&fl);
+ unlock_kernel();
+ break;
+ }
+
+ return error;
+}
+
/**
* lease_get_mtime
* @inode: the inode
@@ -1328,12 +1333,95 @@ asmlinkage long sys_flock(unsigned int f
return error;
}
+/**
+ * vfs_setlock_posix - apply a POSIX lock to a file
+ * @filp: the file to apply the lock to
+ * @fl: the lock to apply
+ *
+ * This function contains the common code to apply a lock to a file.
+ * It's called by the 32- and 64- bit versions of fcntl_setlk and lockd.
+ * If the underlying filesystem provides a locking method, we call that.
+ * Otherwise we lock locally.
+ */
+int vfs_setlock_posix(struct file *filp, struct file_lock *fl)
+{
+ struct lock_operations *l_op = NULL;
+ int (*lock_file)(struct file *, struct file_lock *) = posix_lock_file;
+ int error;
+
+ switch (fl->fl_type) {
+ case F_RDLCK:
+ if (!(filp->f_mode & FMODE_READ))
+ return -EBADF;
+ break;
+ case F_WRLCK:
+ if (!(filp->f_mode & FMODE_WRITE))
+ return -EBADF;
+ break;
+ case F_UNLCK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ error = security_file_lock(filp, fl->fl_type);
+ if (error)
+ return error;
+
+ if (filp->f_op)
+ l_op = filp->f_op->lock;
+ if (l_op && l_op->set_lock)
+ lock_file = l_op->set_lock;
+
+ for (;;) {
+ error = lock_file(filp, fl);
+ if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP))
+ break;
+ if (fl->fl_flags & FL_LOCKD)
+ break;
+ error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
+ if (!error)
+ continue;
+
+ lock_kernel();
+ locks_delete_block(fl);
+ unlock_kernel();
+ break;
+ }
+
+ return error;
+}
+
+/**
+ * vfs_testlock_posix - Report any conflicting lock
+ * @filp: the file to examine
+ * @fl: The lock to report any conflicts against
+ *
+ * This function contains the common code to test for conflicting locks.
+ * It's called by the 32- and 64- bit versions of fcntl getlk and by lockd.
+ * Note that this is an inherently racy interface and the only reason to use
+ * it is to implement the user interface. In-kernel users should attempt to
+ * apply the lock and deal with the failure.
+ */
+void vfs_testlock_posix(struct file *filp, struct file_lock *fl)
+{
+ struct lock_operations *l_op = NULL;
+
+ if (filp->f_op)
+ l_op = filp->f_op->lock;
+ if (l_op && l_op->get_lock) {
+ l_op->get_lock(filp, fl);
+ } else {
+ posix_test_lock(filp, fl);
+ }
+}
+
/* 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 *l)
{
- struct file_lock *fl, file_lock;
+ struct file_lock file_lock;
struct flock flock;
int error;
@@ -1344,49 +1432,38 @@ int fcntl_getlk(struct file *filp, struc
if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
goto out;
+ locks_init_lock(&file_lock);
error = flock_to_posix_lock(filp, &file_lock, &flock);
if (error)
goto out;
- if (filp->f_op && filp->f_op->lock) {
- error = filp->f_op->lock(filp, F_GETLK, &file_lock);
- if (error < 0)
- goto out;
- else if (error == LOCK_USE_CLNT)
- /* Bypass for NFS with no locking - 2.0.36 compat */
- fl = posix_test_lock(filp, &file_lock);
- else
- fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
- } else {
- fl = posix_test_lock(filp, &file_lock);
- }
+ vfs_testlock_posix(filp, &file_lock);
- flock.l_type = F_UNLCK;
- if (fl != NULL) {
- flock.l_pid = fl->fl_pid;
+ flock.l_type = file_lock.fl_type;
+ if (flock.l_type != F_UNLCK) {
#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)
+ if (file_lock.fl_start > OFFT_OFFSET_MAX)
goto out;
- if ((fl->fl_end != OFFSET_MAX)
- && (fl->fl_end > OFFT_OFFSET_MAX))
+ if ((file_lock.fl_end != OFFSET_MAX)
+ && (file_lock.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_pid = file_lock.fl_pid;
+ flock.l_start = file_lock.fl_start;
+ flock.l_len = file_lock.fl_end == OFFSET_MAX ? 0 :
+ file_lock.fl_end - file_lock.fl_start + 1;
flock.l_whence = 0;
- flock.l_type = fl->fl_type;
}
error = -EFAULT;
if (!copy_to_user(l, &flock, sizeof(flock)))
error = 0;
-out:
+ out:
return error;
}
@@ -1395,14 +1472,11 @@ out:
*/
int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock *l)
{
- struct file_lock *file_lock = locks_alloc_lock(0);
+ struct file_lock file_lock;
struct flock flock;
struct inode *inode;
int error;
- if (file_lock == NULL)
- return -ENOLCK;
-
/*
* This might block, so we do it before checking the inode.
*/
@@ -1425,57 +1499,17 @@ int fcntl_setlk(struct file *filp, unsig
}
}
- error = flock_to_posix_lock(filp, file_lock, &flock);
+ locks_init_lock(&file_lock);
+ error = flock_to_posix_lock(filp, &file_lock, &flock);
if (error)
goto out;
if (cmd == F_SETLKW) {
- file_lock->fl_flags |= FL_SLEEP;
+ file_lock.fl_flags |= FL_SLEEP;
}
-
- error = -EBADF;
- switch (flock.l_type) {
- case F_RDLCK:
- if (!(filp->f_mode & FMODE_READ))
- goto out;
- break;
- case F_WRLCK:
- if (!(filp->f_mode & FMODE_WRITE))
- goto out;
- break;
- case F_UNLCK:
- break;
- default:
- error = -EINVAL;
- goto out;
- }
-
- error = security_file_lock(filp, file_lock->fl_type);
- if (error)
- goto out;
-
- if (filp->f_op && filp->f_op->lock != NULL) {
- error = filp->f_op->lock(filp, cmd, file_lock);
- if (error < 0)
- goto out;
- }
-
- 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;
- lock_kernel();
- locks_delete_block(file_lock);
- unlock_kernel();
- break;
- }
+ error = vfs_setlock_posix(filp, &file_lock);
out:
- locks_free_lock(file_lock);
return error;
}
@@ -1485,7 +1519,7 @@ int fcntl_setlk(struct file *filp, unsig
*/
int fcntl_getlk64(struct file *filp, struct flock64 *l)
{
- struct file_lock *fl, file_lock;
+ struct file_lock file_lock;
struct flock64 flock;
int error;
@@ -1496,31 +1530,20 @@ int fcntl_getlk64(struct file *filp, str
if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
goto out;
+ locks_init_lock(&file_lock);
error = flock64_to_posix_lock(filp, &file_lock, &flock);
if (error)
goto out;
- if (filp->f_op && filp->f_op->lock) {
- error = filp->f_op->lock(filp, F_GETLK, &file_lock);
- if (error < 0)
- goto out;
- else if (error == LOCK_USE_CLNT)
- /* Bypass for NFS with no locking - 2.0.36 compat */
- fl = posix_test_lock(filp, &file_lock);
- else
- fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
- } else {
- fl = posix_test_lock(filp, &file_lock);
- }
+ vfs_testlock_posix(filp, &file_lock);
- 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_type = file_lock.fl_type;
+ if (flock.l_type != F_UNLCK) {
+ flock.l_pid = file_lock.fl_pid;
+ flock.l_start = file_lock.fl_start;
+ flock.l_len = file_lock.fl_end == OFFSET_MAX ? 0 :
+ file_lock.fl_end - file_lock.fl_start + 1;
flock.l_whence = 0;
- flock.l_type = fl->fl_type;
}
error = -EFAULT;
if (!copy_to_user(l, &flock, sizeof(flock)))
@@ -1535,14 +1558,11 @@ out:
*/
int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 *l)
{
- struct file_lock *file_lock = locks_alloc_lock(0);
+ struct file_lock file_lock;
struct flock64 flock;
struct inode *inode;
int error;
- if (file_lock == NULL)
- return -ENOLCK;
-
/*
* This might block, so we do it before checking the inode.
*/
@@ -1565,57 +1585,16 @@ int fcntl_setlk64(struct file *filp, uns
}
}
- error = flock64_to_posix_lock(filp, file_lock, &flock);
+ error = flock64_to_posix_lock(filp, &file_lock, &flock);
if (error)
goto out;
if (cmd == F_SETLKW64) {
- file_lock->fl_flags |= FL_SLEEP;
- }
-
- error = -EBADF;
- switch (flock.l_type) {
- case F_RDLCK:
- if (!(filp->f_mode & FMODE_READ))
- goto out;
- break;
- case F_WRLCK:
- if (!(filp->f_mode & FMODE_WRITE))
- goto out;
- break;
- case F_UNLCK:
- break;
- default:
- error = -EINVAL;
- goto out;
- }
-
- error = security_file_lock(filp, file_lock->fl_type);
- if (error)
- goto out;
-
- if (filp->f_op && filp->f_op->lock != NULL) {
- error = filp->f_op->lock(filp, cmd, file_lock);
- if (error < 0)
- goto out;
+ file_lock.fl_flags |= FL_SLEEP;
}
- 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;
-
- lock_kernel();
- locks_delete_block(file_lock);
- unlock_kernel();
- break;
- }
+ error = vfs_setlock_posix(filp, &file_lock);
-out:
- locks_free_lock(file_lock);
+ out:
return error;
}
#endif /* BITS_PER_LONG == 32 */
@@ -1627,30 +1606,34 @@ out:
*/
void locks_remove_posix(struct file *filp, fl_owner_t owner)
{
- struct file_lock lock;
+ struct file_lock *fl, **before;
+ struct lock_operations *l_op = NULL;
+ struct inode *inode = filp->f_dentry->d_inode;
/*
- * If there are no locks held on this file, we don't need to call
- * posix_lock_file(). Another process could be setting a lock on this
- * file at the same time, but we wouldn't remove that lock anyway.
+ * If there are no locks held on this file, we don't need to do
+ * anything. Another thread could be setting a lock on this
+ * file at the same time, but it's a race we just won.
*/
- if (!filp->f_dentry->d_inode->i_flock)
+ if (!inode->i_flock)
return;
- lock.fl_type = F_UNLCK;
- lock.fl_flags = FL_POSIX;
- lock.fl_start = 0;
- lock.fl_end = OFFSET_MAX;
- lock.fl_owner = owner;
- lock.fl_pid = current->tgid;
- lock.fl_file = filp;
-
- if (filp->f_op && filp->f_op->lock != NULL) {
- filp->f_op->lock(filp, F_SETLK, &lock);
- /* Ignore any error -- we must remove the locks anyway */
+ if (filp->f_op)
+ l_op = filp->f_op->lock;
+ if (l_op && l_op->remove_posix) {
+ l_op->remove_posix(filp, owner);
+ } else {
+ lock_kernel();
+ before = &inode->i_flock;
+ while ((fl = *before) != NULL) {
+ if (IS_POSIX(fl) && fl->fl_owner == owner) {
+ locks_delete_lock(before);
+ continue;
+ }
+ before = &fl->fl_next;
+ }
+ unlock_kernel();
}
-
- posix_lock_file(filp, &lock);
}
/*
diff -urpNX dontdiff linux-2.5.52/fs/nfs/file.c linux-2.5.52-flock/fs/nfs/file.c
--- linux-2.5.52/fs/nfs/file.c 2002-11-17 23:29:49.000000000 -0500
+++ linux-2.5.52-flock/fs/nfs/file.c 2002-12-18 14:03:18.000000000 -0500
@@ -40,20 +40,6 @@ static ssize_t nfs_file_write(struct kio
static int nfs_file_flush(struct file *);
static int nfs_fsync(struct file *, struct dentry *dentry, int datasync);
-struct file_operations nfs_file_operations = {
- .llseek = remote_llseek,
- .read = do_sync_read,
- .write = do_sync_write,
- .aio_read = nfs_file_read,
- .aio_write = nfs_file_write,
- .mmap = nfs_file_mmap,
- .open = nfs_open,
- .flush = nfs_file_flush,
- .release = nfs_release,
- .fsync = nfs_fsync,
- .lock = nfs_lock,
-};
-
struct inode_operations nfs_file_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
@@ -239,10 +225,13 @@ nfs_lock(struct file *filp, int cmd, str
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
return -ENOLCK;
- /* Fake OK code if mounted without NLM support */
+ /* lock locally if mounted without NLM support */
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
- if (IS_GETLK(cmd))
- status = LOCK_USE_CLNT;
+ if (IS_GETLK(cmd)) {
+ vfs_testlock_posix(filp, fl);
+ } else {
+ status = vfs_setlock_posix(filp, fl);
+ }
goto out_ok;
}
@@ -295,3 +284,52 @@ nfs_lock(struct file *filp, int cmd, str
}
return status;
}
+
+static int nfs_set_lock(struct file *filp, struct file_lock *fl)
+{
+ if (fl->fl_flags & FL_SLEEP) {
+ return nfs_lock(filp, F_SETLKW, fl);
+ } else {
+ return nfs_lock(filp, F_SETLK, fl);
+ }
+}
+
+static void nfs_get_lock(struct file *filp, struct file_lock *fl)
+{
+ int status = nfs_lock(filp, F_GETLK, fl);
+ if (status) {
+ fl->fl_type = F_UNLCK;
+ }
+}
+
+#if 0
+static int nfs_remove_posix(struct file *filp, fl_owner_t id)
+{
+ printk("remove posix support not yet implemented\n");
+}
+#endif
+
+extern void nlmclnt_insert_lock_callback(struct file_lock *fl);
+extern void nlmclnt_remove_lock_callback(struct file_lock *fl);
+
+static struct lock_operations nfs_client_lops = {
+ .set_lock = nfs_set_lock,
+ .get_lock = nfs_get_lock,
+/* .remove_posix = nfs_remove_posix, */
+ .lock_insert = nlmclnt_insert_lock_callback,
+ .lock_remove = nlmclnt_remove_lock_callback,
+};
+
+struct file_operations nfs_file_operations = {
+ .llseek = remote_llseek,
+ .read = do_sync_read,
+ .write = do_sync_write,
+ .aio_read = nfs_file_read,
+ .aio_write = nfs_file_write,
+ .mmap = nfs_file_mmap,
+ .open = nfs_open,
+ .flush = nfs_file_flush,
+ .release = nfs_release,
+ .fsync = nfs_fsync,
+ .lock = &nfs_client_lops,
+};
diff -urpNX dontdiff linux-2.5.52/include/linux/fs.h linux-2.5.52-flock/include/linux/fs.h
--- linux-2.5.52/include/linux/fs.h 2002-12-18 14:00:57.000000000 -0500
+++ linux-2.5.52-flock/include/linux/fs.h 2002-12-18 14:03:18.000000000 -0500
@@ -536,8 +536,6 @@ struct file_lock {
loff_t fl_end;
void (*fl_notify)(struct file_lock *); /* unblock callback */
- void (*fl_insert)(struct file_lock *); /* lock insertion callback */
- void (*fl_remove)(struct file_lock *); /* lock removal callback */
struct fasync_struct * fl_fasync; /* for lease break notifications */
unsigned long fl_break_time; /* for nonblocking lease breaks */
@@ -567,13 +565,12 @@ extern int fcntl_setlk64(struct file *,
/* fs/locks.c */
extern void locks_init_lock(struct file_lock *);
extern void locks_copy_lock(struct file_lock *, struct file_lock *);
+extern int vfs_setlock_posix(struct file *, struct file_lock *);
+extern void vfs_testlock_posix(struct file *, struct file_lock *);
extern void locks_remove_posix(struct file *, fl_owner_t);
extern void locks_remove_flock(struct file *);
-extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
-extern int posix_lock_file(struct file *, struct file_lock *);
extern void posix_block_lock(struct file_lock *, struct file_lock *);
extern void posix_unblock_lock(struct file *, struct file_lock *);
-extern int posix_locks_deadlock(struct file_lock *, struct file_lock *);
extern int __break_lease(struct inode *inode, unsigned int flags);
extern void lease_get_mtime(struct inode *, struct timespec *time);
extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
@@ -731,6 +728,42 @@ typedef struct {
typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long);
+/**
+ * struct lock_operations - filesystem hooks for file locking
+ *
+ * This struct is a work in progress. It is intended to be per-filesystem;
+ * indeed it could be part of f_ops were it not pure bloat for non-network
+ * filesystems. I suspect lock_insert and lock_remove are now unnecessary,
+ * but need feedback from FS maintainers.
+ *
+ * @set_lock:
+ * Attempt to set a new lock. BKL not held, may sleep.
+ * @get_lock:
+ * Return any lock which would conflict with the incoming lock.
+ * No locks held, may sleep.
+ * @remove_posix:
+ * A process closed a file descriptor. Any locks on this @filp owned
+ * by @owner should be removed. BKL not held, may sleep.
+ * @remove_flock:
+ * This @filp has been closed. All locks owned by this process should
+ * be removed. BKL not held, may sleep.
+ * @lock_insert:
+ * Notification that @fl, which was previously blocked, is now being
+ * inserted. BKL might not be held. Must not sleep.
+ * @lock_remove:
+ * Notification that @fl, which was an active lock, is now being
+ * removed from the @filp. BKL might not be held. Must not sleep.
+ */
+
+struct lock_operations {
+ int (*set_lock) (struct file *filp, struct file_lock *fl);
+ void (*get_lock) (struct file *filp, struct file_lock *fl);
+ void (*remove_posix) (struct file *filp, fl_owner_t owner);
+ void (*remove_flock) (struct file *filp);
+ void (*lock_insert) (struct file_lock *fl);
+ void (*lock_remove) (struct file_lock *fl);
+};
+
/*
* NOTE:
* read, write, poll, fsync, readv, writev can be called
@@ -754,7 +787,7 @@ struct file_operations {
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
- int (*lock) (struct file *, int, struct file_lock *);
+ struct lock_operations *lock;
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
@@ -1002,11 +1035,6 @@ extern long do_mount(char *, char *, cha
extern int vfs_statfs(struct super_block *, struct statfs *);
-/* Return value for VFS lock functions - tells locks.c to lock conventionally
- * REALLY kosha for root NFS and nfs_lock
- */
-#define LOCK_USE_CLNT 1
-
#define FLOCK_VERIFY_READ 1
#define FLOCK_VERIFY_WRITE 2
diff -urpNX dontdiff linux-2.5.52/kernel/ksyms.c linux-2.5.52-flock/kernel/ksyms.c
--- linux-2.5.52/kernel/ksyms.c 2002-12-18 14:00:58.000000000 -0500
+++ linux-2.5.52-flock/kernel/ksyms.c 2002-12-18 14:03:18.000000000 -0500
@@ -232,11 +232,10 @@ EXPORT_SYMBOL(generic_ro_fops);
EXPORT_SYMBOL(file_lock_list);
EXPORT_SYMBOL(locks_init_lock);
EXPORT_SYMBOL(locks_copy_lock);
-EXPORT_SYMBOL(posix_lock_file);
-EXPORT_SYMBOL(posix_test_lock);
+EXPORT_SYMBOL(vfs_setlock_posix);
+EXPORT_SYMBOL(vfs_testlock_posix);
EXPORT_SYMBOL(posix_block_lock);
EXPORT_SYMBOL(posix_unblock_lock);
-EXPORT_SYMBOL(posix_locks_deadlock);
EXPORT_SYMBOL(locks_mandatory_area);
EXPORT_SYMBOL(dput);
EXPORT_SYMBOL(have_submounts);