[PATCH] locks: breaking read lease should not block read open

J. Bruce Fields bfields at fieldses.org
Thu Jun 9 17:16:06 MDT 2011


The lease code behavior during the lease-breaking process is strange.
Fixing it completely would be complicated by the fact that the current
code allows a lease break to downgrade the lease instead of necessarily
removing it.

But I can't see what the point of that feature is.  And googling around
and looking at the Samba code, I can't see any evidence that anyone uses
it.  Think we could just do away with removing the ability to downgrade
to satisfy a lease break?

--b.

commit 70fc3ee9d3e9d61203d4310db4a8128886747272
Author: J. Bruce Fields <bfields at redhat.com>
Date:   Thu Jun 9 09:31:30 2011 -0400

    locks: breaking read lease should not block read open
    
    Currently read opens block while a lease break is in progress, even if
    the lease being broken was only a read lease.
    
    This is an annoyance for v4, since clients may need to do read opens
    before they can return a delegation.
    
    This happens because we use fl_type on a breaking lease to track the
    type it will have when the break is finished (F_RDLCK or F_UNLCK) as
    opposed to the type it had before the break started, so we no longer
    even know at that point whether it was a write lease or a read lease.
    
    The only reason we do that is to allow a write lease broken by a read
    open to be downgraded to a read lease instead of removed completely.
    
    However, that doesn't seem like a useful feature--as far as I can tell,
    nobody uses it or likely ever will.
    
    So, just keep the original lease type in fl_type.
    
    Reported-by: Casey Bodley <cbodley at citi.umich.edu>
    Signed-off-by: J. Bruce Fields <bfields at redhat.com>

diff --git a/fs/locks.c b/fs/locks.c
index 0a4f50d..171391f 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1158,9 +1158,9 @@ static void time_out_leases(struct inode *inode)
 			before = &fl->fl_next;
 			continue;
 		}
-		lease_modify(before, fl->fl_type & ~F_INPROGRESS);
-		if (fl == *before)	/* lease_modify may have freed fl */
-			before = &fl->fl_next;
+		lease_modify(before, F_UNLCK);
+		/* lease_modify has freed fl */
+		before = &fl->fl_next;
 	}
 }
 
@@ -1176,7 +1176,7 @@ static void time_out_leases(struct inode *inode)
  */
 int __break_lease(struct inode *inode, unsigned int mode)
 {
-	int error = 0, future;
+	int error = 0;
 	struct file_lock *new_fl, *flock;
 	struct file_lock *fl;
 	unsigned long break_time;
@@ -1197,19 +1197,8 @@ int __break_lease(struct inode *inode, unsigned int mode)
 		if (fl->fl_owner == current->files)
 			i_have_this_lease = 1;
 
-	if (want_write) {
-		/* If we want write access, we have to revoke any lease. */
-		future = F_UNLCK | F_INPROGRESS;
-	} else if (flock->fl_type & F_INPROGRESS) {
-		/* If the lease is already being broken, we just leave it */
-		future = flock->fl_type;
-	} else if (flock->fl_type & F_WRLCK) {
-		/* Downgrade the exclusive lease to a read-only lease. */
-		future = F_RDLCK | F_INPROGRESS;
-	} else {
-		/* the existing lease was read-only, so we can read too. */
-		goto out;
-	}
+	if (!want_write && !(flock->fl_type & F_WRLCK))
+		goto out; /* no conflict */
 
 	if (IS_ERR(new_fl) && !i_have_this_lease
 			&& ((mode & O_NONBLOCK) == 0)) {
@@ -1225,8 +1214,8 @@ int __break_lease(struct inode *inode, unsigned int mode)
 	}
 
 	for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
-		if (fl->fl_type != future) {
-			fl->fl_type = future;
+		if (!(fl->fl_type & F_INPROGRESS)) {
+			fl->fl_type |= F_INPROGRESS;
 			fl->fl_break_time = break_time;
 			/* lease must have lmops break callback */
 			fl->fl_lmops->fl_break(fl);
@@ -1254,10 +1243,10 @@ restart:
 	if (error >= 0) {
 		if (error == 0)
 			time_out_leases(inode);
-		/* Wait for the next lease that has not been broken yet */
+		/* Wait for the next lease that conflicts */
 		for (flock = inode->i_flock; flock && IS_LEASE(flock);
 				flock = flock->fl_next) {
-			if (flock->fl_type & F_INPROGRESS)
+			if (want_write || flock->fl_type & F_WRLCK)
 				goto restart;
 		}
 		error = 0;
@@ -1390,11 +1379,10 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
 			before = &fl->fl_next) {
 		if (fl->fl_file == filp)
 			my_before = before;
-		else if (fl->fl_type == (F_INPROGRESS | F_UNLCK))
+		else if (fl->fl_type & F_INPROGRESS)
 			/*
-			 * Someone is in the process of opening this
-			 * file for writing so we may not take an
-			 * exclusive lease on it.
+			 * Don't allow new leases while any lease is
+			 * being broken:
 			 */
 			wrlease_count++;
 		else


More information about the samba-technical mailing list