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 , June, 2000. */ -#include -#include -#include -#include #include -#include -#include +#include #include +#include +#include #include +#include +#include +#include +#include +#include -#include #include #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); .