aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2011-07-26 18:25:49 -0400
committerJ. Bruce Fields <bfields@redhat.com>2011-08-19 13:25:34 -0400
commit778fc546f749c588aa2f6cd50215d2715c374252 (patch)
treeb3ffa04327884cd0491c3ee1677f0ffc589ebe9c /fs
parent710b7216964d6455cf1b215c43b03a1a79008c7d (diff)
locks: fix tracking of inprogress lease breaks
We currently use a bit in fl_flags to record whether a lease is being broken, and set fl_type to the type (RDLCK or UNLCK) that it will eventually have. This means that once the lease break starts, we forget what the lease's type *used* to be. Breaking a read lease will then result in blocking read opens, even though there's no conflict--because the lease type is now F_UNLCK and we can no longer tell whether it was previously a read or write lease. So, instead keep fl_type as the original type (the type which we enforce), and keep track of whether we're unlocking or merely downgrading by replacing the single FL_INPROGRESS flag by FL_UNLOCK_PENDING and FL_DOWNGRADE_PENDING flags. To get this right we also need to track separate downgrade and break times, to handle the case where a write-leased file gets conflicting opens first for read, then later for write. (I first considered just eliminating the downgrade behavior completely--nfsv4 doesn't need it, and nobody as far as I can tell actually uses it currently--but Jeremy Allison tells me that Windows oplocks do behave this way, so Samba will probably use this some day.) Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/locks.c87
1 files changed, 55 insertions, 32 deletions
diff --git a/fs/locks.c b/fs/locks.c
index c4215418bca3..c525aa4de234 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -135,7 +135,16 @@
135 135
136static bool lease_breaking(struct file_lock *fl) 136static bool lease_breaking(struct file_lock *fl)
137{ 137{
138 return fl->fl_flags & FL_INPROGRESS; 138 return fl->fl_flags & (FL_UNLOCK_PENDING | FL_DOWNGRADE_PENDING);
139}
140
141static int target_leasetype(struct file_lock *fl)
142{
143 if (fl->fl_flags & FL_UNLOCK_PENDING)
144 return F_UNLCK;
145 if (fl->fl_flags & FL_DOWNGRADE_PENDING)
146 return F_RDLCK;
147 return fl->fl_type;
139} 148}
140 149
141int leases_enable = 1; 150int leases_enable = 1;
@@ -1124,6 +1133,17 @@ int locks_mandatory_area(int read_write, struct inode *inode,
1124 1133
1125EXPORT_SYMBOL(locks_mandatory_area); 1134EXPORT_SYMBOL(locks_mandatory_area);
1126 1135
1136static void lease_clear_pending(struct file_lock *fl, int arg)
1137{
1138 switch (arg) {
1139 case F_UNLCK:
1140 fl->fl_flags &= ~FL_UNLOCK_PENDING;
1141 /* fall through: */
1142 case F_RDLCK:
1143 fl->fl_flags &= ~FL_DOWNGRADE_PENDING;
1144 }
1145}
1146
1127/* We already had a lease on this file; just change its type */ 1147/* We already had a lease on this file; just change its type */
1128int lease_modify(struct file_lock **before, int arg) 1148int lease_modify(struct file_lock **before, int arg)
1129{ 1149{
@@ -1132,7 +1152,7 @@ int lease_modify(struct file_lock **before, int arg)
1132 1152
1133 if (error) 1153 if (error)
1134 return error; 1154 return error;
1135 fl->fl_flags &= ~FL_INPROGRESS; 1155 lease_clear_pending(fl, arg);
1136 locks_wake_up_blocks(fl); 1156 locks_wake_up_blocks(fl);
1137 if (arg == F_UNLCK) 1157 if (arg == F_UNLCK)
1138 locks_delete_lock(before); 1158 locks_delete_lock(before);
@@ -1141,6 +1161,14 @@ int lease_modify(struct file_lock **before, int arg)
1141 1161
1142EXPORT_SYMBOL(lease_modify); 1162EXPORT_SYMBOL(lease_modify);
1143 1163
1164static bool past_time(unsigned long then)
1165{
1166 if (!then)
1167 /* 0 is a special value meaning "this never expires": */
1168 return false;
1169 return time_after(jiffies, then);
1170}
1171
1144static void time_out_leases(struct inode *inode) 1172static void time_out_leases(struct inode *inode)
1145{ 1173{
1146 struct file_lock **before; 1174 struct file_lock **before;
@@ -1148,12 +1176,10 @@ static void time_out_leases(struct inode *inode)
1148 1176
1149 before = &inode->i_flock; 1177 before = &inode->i_flock;
1150 while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) { 1178 while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) {
1151 if ((fl->fl_break_time == 0) 1179 if (past_time(fl->fl_downgrade_time))
1152 || time_before(jiffies, fl->fl_break_time)) { 1180 lease_modify(before, F_RDLCK);
1153 before = &fl->fl_next; 1181 if (past_time(fl->fl_break_time))
1154 continue; 1182 lease_modify(before, F_UNLCK);
1155 }
1156 lease_modify(before, fl->fl_type);
1157 if (fl == *before) /* lease_modify may have freed fl */ 1183 if (fl == *before) /* lease_modify may have freed fl */
1158 before = &fl->fl_next; 1184 before = &fl->fl_next;
1159 } 1185 }
@@ -1171,7 +1197,7 @@ static void time_out_leases(struct inode *inode)
1171 */ 1197 */
1172int __break_lease(struct inode *inode, unsigned int mode) 1198int __break_lease(struct inode *inode, unsigned int mode)
1173{ 1199{
1174 int error = 0, future; 1200 int error = 0;
1175 struct file_lock *new_fl, *flock; 1201 struct file_lock *new_fl, *flock;
1176 struct file_lock *fl; 1202 struct file_lock *fl;
1177 unsigned long break_time; 1203 unsigned long break_time;
@@ -1188,24 +1214,13 @@ int __break_lease(struct inode *inode, unsigned int mode)
1188 if ((flock == NULL) || !IS_LEASE(flock)) 1214 if ((flock == NULL) || !IS_LEASE(flock))
1189 goto out; 1215 goto out;
1190 1216
1217 if (!locks_conflict(flock, new_fl))
1218 goto out;
1219
1191 for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) 1220 for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next)
1192 if (fl->fl_owner == current->files) 1221 if (fl->fl_owner == current->files)
1193 i_have_this_lease = 1; 1222 i_have_this_lease = 1;
1194 1223
1195 if (want_write) {
1196 /* If we want write access, we have to revoke any lease. */
1197 future = F_UNLCK;
1198 } else if (lease_breaking(flock)) {
1199 /* If the lease is already being broken, we just leave it */
1200 future = flock->fl_type;
1201 } else if (flock->fl_type & F_WRLCK) {
1202 /* Downgrade the exclusive lease to a read-only lease. */
1203 future = F_RDLCK;
1204 } else {
1205 /* the existing lease was read-only, so we can read too. */
1206 goto out;
1207 }
1208
1209 if (IS_ERR(new_fl) && !i_have_this_lease 1224 if (IS_ERR(new_fl) && !i_have_this_lease
1210 && ((mode & O_NONBLOCK) == 0)) { 1225 && ((mode & O_NONBLOCK) == 0)) {
1211 error = PTR_ERR(new_fl); 1226 error = PTR_ERR(new_fl);
@@ -1220,13 +1235,18 @@ int __break_lease(struct inode *inode, unsigned int mode)
1220 } 1235 }
1221 1236
1222 for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { 1237 for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
1223 if (fl->fl_type != future) { 1238 if (want_write) {
1224 fl->fl_type = future; 1239 if (fl->fl_flags & FL_UNLOCK_PENDING)
1225 fl->fl_flags |= FL_INPROGRESS; 1240 continue;
1241 fl->fl_flags |= FL_UNLOCK_PENDING;
1226 fl->fl_break_time = break_time; 1242 fl->fl_break_time = break_time;
1227 /* lease must have lmops break callback */ 1243 } else {
1228 fl->fl_lmops->lm_break(fl); 1244 if (lease_breaking(flock))
1245 continue;
1246 fl->fl_flags |= FL_DOWNGRADE_PENDING;
1247 fl->fl_downgrade_time = break_time;
1229 } 1248 }
1249 fl->fl_lmops->lm_break(fl);
1230 } 1250 }
1231 1251
1232 if (i_have_this_lease || (mode & O_NONBLOCK)) { 1252 if (i_have_this_lease || (mode & O_NONBLOCK)) {
@@ -1250,10 +1270,13 @@ restart:
1250 if (error >= 0) { 1270 if (error >= 0) {
1251 if (error == 0) 1271 if (error == 0)
1252 time_out_leases(inode); 1272 time_out_leases(inode);
1253 /* Wait for the next lease that has not been broken yet */ 1273 /*
1274 * Wait for the next conflicting lease that has not been
1275 * broken yet
1276 */
1254 for (flock = inode->i_flock; flock && IS_LEASE(flock); 1277 for (flock = inode->i_flock; flock && IS_LEASE(flock);
1255 flock = flock->fl_next) { 1278 flock = flock->fl_next) {
1256 if (lease_breaking(flock)) 1279 if (locks_conflict(new_fl, flock))
1257 goto restart; 1280 goto restart;
1258 } 1281 }
1259 error = 0; 1282 error = 0;
@@ -1321,7 +1344,7 @@ int fcntl_getlease(struct file *filp)
1321 for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl); 1344 for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl);
1322 fl = fl->fl_next) { 1345 fl = fl->fl_next) {
1323 if (fl->fl_file == filp) { 1346 if (fl->fl_file == filp) {
1324 type = fl->fl_type; 1347 type = target_leasetype(fl);
1325 break; 1348 break;
1326 } 1349 }
1327 } 1350 }
@@ -1386,7 +1409,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
1386 before = &fl->fl_next) { 1409 before = &fl->fl_next) {
1387 if (fl->fl_file == filp) 1410 if (fl->fl_file == filp)
1388 my_before = before; 1411 my_before = before;
1389 else if ((fl->fl_type == F_UNLCK) && lease_breaking(fl)) 1412 else if (fl->fl_flags & FL_UNLOCK_PENDING)
1390 /* 1413 /*
1391 * Someone is in the process of opening this 1414 * Someone is in the process of opening this
1392 * file for writing so we may not take an 1415 * file for writing so we may not take an