diff options
author | J. Bruce Fields <bfields@redhat.com> | 2011-07-26 18:25:49 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2011-08-19 13:25:34 -0400 |
commit | 778fc546f749c588aa2f6cd50215d2715c374252 (patch) | |
tree | b3ffa04327884cd0491c3ee1677f0ffc589ebe9c /fs | |
parent | 710b7216964d6455cf1b215c43b03a1a79008c7d (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.c | 87 |
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 | ||
136 | static bool lease_breaking(struct file_lock *fl) | 136 | static 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 | |||
141 | static 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 | ||
141 | int leases_enable = 1; | 150 | int leases_enable = 1; |
@@ -1124,6 +1133,17 @@ int locks_mandatory_area(int read_write, struct inode *inode, | |||
1124 | 1133 | ||
1125 | EXPORT_SYMBOL(locks_mandatory_area); | 1134 | EXPORT_SYMBOL(locks_mandatory_area); |
1126 | 1135 | ||
1136 | static 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 */ |
1128 | int lease_modify(struct file_lock **before, int arg) | 1148 | int 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 | ||
1142 | EXPORT_SYMBOL(lease_modify); | 1162 | EXPORT_SYMBOL(lease_modify); |
1143 | 1163 | ||
1164 | static 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 | |||
1144 | static void time_out_leases(struct inode *inode) | 1172 | static 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 | */ |
1172 | int __break_lease(struct inode *inode, unsigned int mode) | 1198 | int __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 |