diff -urpNX dontdiff linux-2.5.49/fs/lockd/clntproc.c linux-2.5.49-flock/fs/lockd/clntproc.c
--- linux-2.5.49/fs/lockd/clntproc.c 2002-11-19 21:33:24.000000000 -0800
+++ linux-2.5.49-flock/fs/lockd/clntproc.c 2002-11-25 02:41:46.000000000 -0800
@@ -386,27 +386,25 @@ nlmclnt_test(struct nlm_rqst *req, struc
status = req->a_res.status;
if (status == NLM_LCK_GRANTED) {
- fl->fl_type = F_UNLCK;
- } if (status == NLM_LCK_DENIED) {
+ return 0;
+ } else if (status == NLM_LCK_DENIED) {
/*
* Report the conflicting lock back to the application.
* FIXME: Is it OK to report the pid back as well?
*/
locks_copy_lock(fl, &req->a_res.lock.fl);
/* fl->fl_pid = 0; */
+ return 1;
} else {
- return nlm_stat_to_errno(req->a_res.status);
+ return nlm_stat_to_errno(status);
}
-
- 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 +460,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.49/fs/lockd/svclock.c linux-2.5.49-flock/fs/lockd/svclock.c
--- linux-2.5.49/fs/lockd/svclock.c 2002-09-16 07:51:17.000000000 -0700
+++ linux-2.5.49-flock/fs/lockd/svclock.c 2002-11-24 10:28:26.000000000 -0800
@@ -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.49/fs/lockd/svcsubs.c linux-2.5.49-flock/fs/lockd/svcsubs.c
--- linux-2.5.49/fs/lockd/svcsubs.c 2002-09-16 07:51:17.000000000 -0700
+++ linux-2.5.49-flock/fs/lockd/svcsubs.c 2002-11-24 10:28:26.000000000 -0800
@@ -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.49/fs/locks.c linux-2.5.49-flock/fs/locks.c
--- linux-2.5.49/fs/locks.c 2002-11-24 08:18:53.000000000 -0800
+++ linux-2.5.49-flock/fs/locks.c 2002-11-25 07:42:12.000000000 -0800
@@ -114,16 +114,18 @@
* Stephen Rothwell <
[email protected]>, June, 2000.
*/
-#include <linux/slab.h>
+#include <linux/capability.h>
#include <linux/file.h>
-#include <linux/smp_lock.h>
+#include <linux/fs.h>
#include <linux/init.h>
-#include <linux/capability.h>
-#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/time.h>
-#include <linux/fs.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)
@@ -187,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;
}
/*
@@ -219,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;
}
@@ -321,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);
}
@@ -361,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:
@@ -397,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;
@@ -474,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);
}
/*
@@ -493,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;
@@ -504,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);
@@ -516,53 +514,47 @@ static void locks_delete_lock(struct fil
*/
static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
- switch (caller_fl->fl_type) {
- case F_RDLCK:
- return (sys_fl->fl_type == F_WRLCK);
+ if (caller_fl->fl_type == F_WRLCK)
+ return 1;
- case F_WRLCK:
- return (1);
+ if (sys_fl->fl_type == F_WRLCK)
+ return 1;
- default:
- printk(KERN_ERR "locks_conflict(): impossible lock type - %d\n",
- caller_fl->fl_type);
- break;
- }
- return (0); /* This should never happen */
+ return 0;
}
/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific
- * checking before calling the locks_conflict().
+ * checking before calling locks_conflict().
*/
static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
/* POSIX locks owned by the same process do not conflict with
* each other.
*/
- if (!IS_POSIX(sys_fl) || posix_same_owner(caller_fl, sys_fl))
- return (0);
+ if (posix_same_owner(caller_fl, sys_fl))
+ return 0;
/* Check whether they overlap */
if (!locks_overlap(caller_fl, sys_fl))
return 0;
- return (locks_conflict(caller_fl, sys_fl));
+ return locks_conflict(caller_fl, sys_fl);
}
/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific
- * checking before calling the locks_conflict().
+ * checking before calling locks_conflict().
*/
static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
/* FLOCK locks referring to the same filp do not conflict with
* each other.
*/
- if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file))
- return (0);
+ if (caller_fl->fl_file == sys_fl->fl_file)
+ return 0;
if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
return 0;
- return (locks_conflict(caller_fl, sys_fl));
+ return locks_conflict(caller_fl, sys_fl);
}
static int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, int timeout)
@@ -592,21 +584,32 @@ 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. Returns 1 for success, 0 for failure.
+ */
+static int posix_test_lock(struct file *filp, struct file_lock *fl)
{
struct file_lock *cfl;
+ int result = 0;
lock_kernel();
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;
+ result = 1;
+ break;
}
unlock_kernel();
-
- return (cfl);
+ return result;
}
/* This function tests for deadlock condition before putting a process to
@@ -623,7 +626,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;
@@ -651,63 +654,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.
@@ -787,7 +733,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;
@@ -1102,6 +1048,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
@@ -1327,12 +1330,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_ops->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.
+ */
+int 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) {
+ return l_op->get_lock(filp, fl);
+ } else {
+ return 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;
@@ -1343,49 +1429,43 @@ 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);
- }
-
- flock.l_type = F_UNLCK;
- if (fl != NULL) {
- flock.l_pid = fl->fl_pid;
+ error = vfs_testlock_posix(filp, &file_lock);
+ if (error < 0)
+ goto out;
+
+ if (error == 1) {
#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_type = file_lock.fl_type;
+ 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;
+ } else {
+ flock.l_type = F_UNLCK;
}
+
error = -EFAULT;
if (!copy_to_user(l, &flock, sizeof(flock)))
error = 0;
-out:
+ out:
return error;
}
@@ -1394,14 +1474,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.
*/
@@ -1411,7 +1488,6 @@ int fcntl_setlk(struct file *filp, unsig
inode = filp->f_dentry->d_inode;
-#ifdef CONFIG_MMU
/* Don't allow mandatory locks on files that may be memory mapped
* and shared.
*/
@@ -1424,59 +1500,18 @@ int fcntl_setlk(struct file *filp, unsig
goto out;
}
}
-#endif
- 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_ops->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;
}
@@ -1486,7 +1521,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;
@@ -1497,31 +1532,24 @@ 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);
- }
-
- 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;
+ error = vfs_testlock_posix(filp, &file_lock);
+ if (error < 0)
+ goto out;
+
+ if (error == 1) {
+ flock.l_type = file_lock.fl_type;
+ 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;
+ } else {
+ flock.l_type = F_UNLCK;
}
error = -EFAULT;
if (!copy_to_user(l, &flock, sizeof(flock)))
@@ -1536,14 +1564,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.
*/
@@ -1566,57 +1591,17 @@ int fcntl_setlk64(struct file *filp, uns
}
}
- error = flock64_to_posix_lock(filp, file_lock, &flock);
+ locks_init_lock(&file_lock);
+ 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;
+ file_lock.fl_flags |= FL_SLEEP;
}
- error = security_ops->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_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 */
@@ -1628,30 +1613,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);
}
/*
@@ -1765,19 +1754,18 @@ static void lock_get_status(char* out, s
? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
: (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
}
-#if WE_CAN_BREAK_LSLK_NOW
if (inode) {
+#if WE_CAN_BREAK_LSLK_NOW
out += sprintf(out, "%d %s:%ld ", fl->fl_pid,
inode->i_sb->s_id, inode->i_ino);
+#else
+ out += sprintf(out, "%d %02x:%02x:%ld ", fl->fl_pid,
+ MAJOR(inode->i_sb->s_dev),
+ MINOR(inode->i_sb->s_dev), inode->i_ino);
+#endif
} else {
out += sprintf(out, "%d <none>:0 ", fl->fl_pid);
}
-#else
- /* kdevname is a broken interface. but we expose it to userspace */
- out += sprintf(out, "%d %s:%ld ", fl->fl_pid,
- inode ? kdevname(to_kdev_t(inode->i_sb->s_dev)) : "<none>",
- inode ? inode->i_ino : 0);
-#endif
if (IS_POSIX(fl)) {
if (fl->fl_end == OFFSET_MAX)
out += sprintf(out, "%Ld EOF\n", fl->fl_start);
diff -urpNX dontdiff linux-2.5.49/fs/nfs/file.c linux-2.5.49-flock/fs/nfs/file.c
--- linux-2.5.49/fs/nfs/file.c 2002-11-15 05:42:21.000000000 -0800
+++ linux-2.5.49-flock/fs/nfs/file.c 2002-11-25 08:15:40.000000000 -0800
@@ -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,
@@ -211,15 +197,30 @@ out_swapfile:
goto out;
}
+static int nfs_flush_pending_writes(struct inode *inode)
+{
+ int status1, status2, status3;
+
+ status1 = filemap_fdatawrite(inode->i_mapping);
+ down(&inode->i_sem);
+ status2 = nfs_wb_all(inode);
+ up(&inode->i_sem);
+ status3 = filemap_fdatawait(inode->i_mapping);
+
+ if (status1)
+ return status1;
+ if (status2)
+ return status2;
+ return status3;
+}
+
/*
* Lock a (portion of) a file
*/
-int
-nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
+static int nfs_set_lock(struct file *filp, struct file_lock *fl)
{
struct inode * inode = filp->f_dentry->d_inode;
- int status = 0;
- int status2;
+ int status;
dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
inode->i_sb->s_id, inode->i_ino,
@@ -239,13 +240,6 @@ 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 */
- if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
- if (IS_GETLK(cmd))
- status = LOCK_USE_CLNT;
- goto out_ok;
- }
-
/*
* No BSD flocks over NFS allowed.
* Note: we could try to fake a POSIX lock request here by
@@ -257,41 +251,93 @@ nfs_lock(struct file *filp, int cmd, str
return -ENOLCK;
/*
- * Flush all pending writes before doing anything
- * with locks..
+ * Flush all pending writes before doing anything with locks..
*/
- status = filemap_fdatawrite(inode->i_mapping);
- down(&inode->i_sem);
- status2 = nfs_wb_all(inode);
- if (!status)
- status = status2;
- up(&inode->i_sem);
- status2 = filemap_fdatawait(inode->i_mapping);
- if (!status)
- status = status2;
+ status = nfs_flush_pending_writes(inode);
if (status < 0)
return status;
- lock_kernel();
- status = nlmclnt_proc(inode, cmd, fl);
- unlock_kernel();
- if (status < 0)
- return status;
-
- status = 0;
+ if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) {
+ lock_kernel();
+ if (fl->fl_flags & FL_SLEEP) {
+ status = nlmclnt_proc(inode, F_SETLKW, fl);
+ } else {
+ status = nlmclnt_proc(inode, F_SETLK, fl);
+ }
+ unlock_kernel();
+ if (status < 0)
+ return status;
+ /* If this fails, the NFS server is broken */
+ status = vfs_setlock_posix(filp, fl);
+ if (status)
+ printk(KERN_CRIT "nfs: File lock succeeded remotely "
+ "but failed locally\n");
+ } else {
+ status = vfs_setlock_posix(filp, fl);
+ }
/*
* Make sure we clear the cache whenever we try to get the lock.
* This makes locking act as a cache coherency point.
*/
out_ok:
- if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
- filemap_fdatawrite(inode->i_mapping);
- down(&inode->i_sem);
- nfs_wb_all(inode); /* we may have slept */
- up(&inode->i_sem);
- filemap_fdatawait(inode->i_mapping);
+ if (fl->fl_type != F_UNLCK) {
+ nfs_flush_pending_writes(inode);
nfs_zap_caches(inode);
}
return status;
}
+
+static int nfs_get_lock(struct file *filp, struct file_lock *fl)
+{
+ int status;
+ struct inode *inode = filp->f_dentry->d_inode;
+
+ BUG_ON(!inode);
+
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
+ status = vfs_testlock_posix(filp, fl);
+ } else {
+ status = nfs_flush_pending_writes(inode);
+ if (status < 0)
+ goto out;
+ lock_kernel();
+ status = nlmclnt_proc(inode, F_GETLK, fl);
+ unlock_kernel();
+ }
+
+ out:
+ return status;
+}
+
+#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.49/include/linux/fs.h linux-2.5.49-flock/include/linux/fs.h
--- linux-2.5.49/include/linux/fs.h 2002-11-24 08:18:54.000000000 -0800
+++ linux-2.5.49-flock/include/linux/fs.h 2002-11-25 01:40:57.000000000 -0800
@@ -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 int 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);
@@ -730,6 +727,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);
+ int (*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
@@ -753,7 +786,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 *);
@@ -1000,11 +1033,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.49/kernel/ksyms.c linux-2.5.49-flock/kernel/ksyms.c
--- linux-2.5.49/kernel/ksyms.c 2002-11-24 08:18:55.000000000 -0800
+++ linux-2.5.49-flock/kernel/ksyms.c 2002-11-24 10:28:26.000000000 -0800
@@ -229,11 +229,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);