diff -u linux-2.5.15-flock/fs/locks.c linux-2.5.15-flock/fs/locks.c
--- linux-2.5.15-flock/fs/locks.c Tue May 21 21:45:51 2002
+++ linux-2.5.15-flock/fs/locks.c Wed May 22 01:13:56 2002
@@ -419,13 +419,20 @@
/* Remove waiter from blocker's block list.
* When blocker ends up pointing to itself then the list is empty.
*/
-static void locks_delete_block(struct file_lock *waiter)
+static inline void __locks_delete_block(struct file_lock *waiter)
{
list_del_init(&waiter->fl_block);
list_del_init(&waiter->fl_link);
waiter->fl_next = NULL;
}
+static void locks_delete_block(struct file_lock *waiter)
+{
+ spin_lock(&file_lock_lock);
+ __locks_delete_block(waiter);
+ spin_unlock(&file_lock_lock);
+}
+
/* Insert waiter into blocker's block list.
* We use a circular list so that processes can be easily woken up in
* the order they blocked. The documentation doesn't require this but
@@ -438,7 +445,7 @@
printk(KERN_ERR "locks_insert_block: removing duplicated lock "
"(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid,
waiter->fl_start, waiter->fl_end, waiter->fl_type);
- locks_delete_block(waiter);
+ __locks_delete_block(waiter);
}
list_add_tail(&waiter->fl_block, &blocker->fl_block);
waiter->fl_next = blocker;
@@ -454,7 +461,7 @@
while (!list_empty(&blocker->fl_block)) {
struct file_lock *waiter = list_entry(blocker->fl_block.next,
struct file_lock, fl_block);
- locks_delete_block(waiter);
+ __locks_delete_block(waiter);
if (waiter->fl_notify)
waiter->fl_notify(waiter);
else
@@ -600,7 +607,7 @@
int result;
locks_insert_block(blocker, waiter);
result = interruptible_sleep_on_locked(&waiter->fl_wait, 0);
- locks_delete_block(waiter);
+ __locks_delete_block(waiter);
return result;
}
@@ -609,7 +616,7 @@
int result;
locks_insert_block(blocker, waiter);
result = interruptible_sleep_on_locked(&waiter->fl_wait, time);
- locks_delete_block(waiter);
+ __locks_delete_block(waiter);
return result;
}
@@ -674,6 +681,26 @@
return 0;
}
+/* This function takes no locks. */
+static int posix_find_conflict(struct inode *inode, struct file_lock *caller)
+{
+ struct file_lock **before;
+ for_each_lock(inode, before) {
+ struct file_lock *fl = *before;
+ if (!IS_POSIX(fl))
+ continue;
+ if (!posix_locks_conflict(caller, fl))
+ continue;
+ if (!(caller->fl_flags & FL_SLEEP))
+ return -EAGAIN;
+ if (posix_locks_deadlock(caller, fl))
+ return -EDEADLK;
+ locks_insert_block(fl, caller);
+ return -EAGAIN;
+ }
+ return 0;
+}
+
int locks_mandatory_locked(struct inode *inode)
{
fl_owner_t owner = current->files;
@@ -697,7 +724,6 @@
struct file *filp, loff_t offset,
size_t count)
{
- struct file_lock **before;
struct file_lock *new_fl = locks_alloc_lock(0);
int error;
@@ -707,47 +733,37 @@
new_fl->fl_owner = current->files;
new_fl->fl_pid = current->pid;
new_fl->fl_file = filp;
- new_fl->fl_flags = FL_POSIX | FL_ACCESS;
+ new_fl->fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP;
new_fl->fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
new_fl->fl_start = offset;
new_fl->fl_end = offset + count - 1;
error = 0;
- spin_lock(&file_lock_lock);
-repeat:
- /* Search the lock list for this inode for locks that conflict with
- * the proposed read/write.
- */
- for_each_lock(inode, before) {
- struct file_lock *fl = *before;
- if (!IS_POSIX(fl))
- continue;
- if (fl->fl_start > new_fl->fl_end)
+ for (;;) {
+ spin_lock(&file_lock_lock);
+ error = posix_find_conflict(filp->f_dentry->d_inode, new_fl);
+ spin_unlock(&file_lock_lock);
+ if (error != -EAGAIN)
break;
- if (!posix_locks_conflict(new_fl, fl))
- continue;
+ error = wait_event_interruptible(new_fl->fl_wait, !new_fl->fl_next);
- error = -EAGAIN;
- if (filp && (filp->f_flags & O_NONBLOCK))
- break;
- error = -EDEADLK;
- if (posix_locks_deadlock(new_fl, fl))
- break;
-
- error = locks_block_on(fl, new_fl);
- if (error != 0)
+ /* Permissions may have changed while we slept */
+ if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID) {
+ if (error) {
+ locks_delete_block(new_fl);
+ }
+ error = 0;
break;
+ }
- /*
- * If we've been sleeping someone might have
- * changed the permissions behind our back.
- */
- if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)
- break;
- goto repeat;
+ if (!error)
+ continue;
+
+ locks_delete_block(new_fl);
+ break;
}
- spin_unlock(&file_lock_lock);
+
locks_free_lock(new_fl);
return error;
}
@@ -801,7 +817,7 @@
}
goto out;
}
- locks_insert_lock(&inode->i_flock, new_fl);
+ locks_insert_lock(before, new_fl);
error = 0;
out:
@@ -903,22 +919,9 @@
spin_lock(&file_lock_lock);
if (caller->fl_type != F_UNLCK) {
- for_each_lock(inode, before) {
- struct file_lock *fl = *before;
- if (!IS_POSIX(fl))
- continue;
- if (!posix_locks_conflict(caller, fl))
- continue;
- error = -EAGAIN;
- if (!(caller->fl_flags & FL_SLEEP))
- goto out;
- error = -EDEADLK;
- if (posix_locks_deadlock(caller, fl))
- goto out;
- error = -EAGAIN;
- locks_insert_block(fl, caller);
+ error = posix_find_conflict(inode, caller);
+ if (error)
goto out;
- }
}
for_each_lock(inode, before) {
@@ -1247,9 +1250,7 @@
if (!error)
continue;
- spin_lock(&file_lock_lock);
locks_delete_block(lock);
- spin_unlock(&file_lock_lock);
break;
}
@@ -1397,9 +1398,7 @@
if (!error)
continue;
- spin_lock(&file_lock_lock);
locks_delete_block(file_lock);
- spin_unlock(&file_lock_lock);
break;
}
@@ -1532,9 +1531,7 @@
if (!error)
continue;
- spin_lock(&file_lock_lock);
locks_delete_block(file_lock);
- spin_unlock(&file_lock_lock);
break;
}
@@ -1603,7 +1600,9 @@
void
posix_block_lock(struct file_lock *blocker, struct file_lock *waiter)
{
+ spin_lock(&file_lock_lock);
locks_insert_block(blocker, waiter);
+ spin_unlock(&file_lock_lock);
}
/**
@@ -1615,8 +1614,10 @@
void
posix_unblock_lock(struct file_lock *waiter)
{
+ spin_lock(&file_lock_lock);
if (!list_empty(&waiter->fl_block))
- locks_delete_block(waiter);
+ __locks_delete_block(waiter);
+ spin_unlock(&file_lock_lock);
}
static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)