diff options
author | J. Bruce Fields <bfields@redhat.com> | 2012-03-05 13:18:59 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-11-09 00:16:41 -0500 |
commit | df4e8d2c1d2bbbbace706bfe5417320c9e3fbee3 (patch) | |
tree | 0a56a71e11f39a638be30064411dc6c3363114b6 /fs | |
parent | 617588d5186c887eb94321b021bb5a46f896f4b3 (diff) |
locks: implement delegations
Implement NFSv4 delegations at the vfs level using the new FL_DELEG lock
type.
Note nfsd is the only delegation user and is only using read
delegations. Warn on any attempt to set a write delegation for now.
We'll come back to that case later.
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/locks.c | 55 |
1 files changed, 45 insertions, 10 deletions
diff --git a/fs/locks.c b/fs/locks.c index 079abcd8a836..f99d52bdd05a 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
@@ -1292,28 +1292,40 @@ static void time_out_leases(struct inode *inode) | |||
1292 | } | 1292 | } |
1293 | } | 1293 | } |
1294 | 1294 | ||
1295 | static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker) | ||
1296 | { | ||
1297 | if ((breaker->fl_flags & FL_DELEG) && (lease->fl_flags & FL_LEASE)) | ||
1298 | return false; | ||
1299 | return locks_conflict(breaker, lease); | ||
1300 | } | ||
1301 | |||
1295 | /** | 1302 | /** |
1296 | * __break_lease - revoke all outstanding leases on file | 1303 | * __break_lease - revoke all outstanding leases on file |
1297 | * @inode: the inode of the file to return | 1304 | * @inode: the inode of the file to return |
1298 | * @mode: the open mode (read or write) | 1305 | * @mode: O_RDONLY: break only write leases; O_WRONLY or O_RDWR: |
1306 | * break all leases | ||
1307 | * @type: FL_LEASE: break leases and delegations; FL_DELEG: break | ||
1308 | * only delegations | ||
1299 | * | 1309 | * |
1300 | * break_lease (inlined for speed) has checked there already is at least | 1310 | * break_lease (inlined for speed) has checked there already is at least |
1301 | * some kind of lock (maybe a lease) on this file. Leases are broken on | 1311 | * some kind of lock (maybe a lease) on this file. Leases are broken on |
1302 | * a call to open() or truncate(). This function can sleep unless you | 1312 | * a call to open() or truncate(). This function can sleep unless you |
1303 | * specified %O_NONBLOCK to your open(). | 1313 | * specified %O_NONBLOCK to your open(). |
1304 | */ | 1314 | */ |
1305 | int __break_lease(struct inode *inode, unsigned int mode) | 1315 | int __break_lease(struct inode *inode, unsigned int mode, unsigned int type) |
1306 | { | 1316 | { |
1307 | int error = 0; | 1317 | int error = 0; |
1308 | struct file_lock *new_fl, *flock; | 1318 | struct file_lock *new_fl, *flock; |
1309 | struct file_lock *fl; | 1319 | struct file_lock *fl; |
1310 | unsigned long break_time; | 1320 | unsigned long break_time; |
1311 | int i_have_this_lease = 0; | 1321 | int i_have_this_lease = 0; |
1322 | bool lease_conflict = false; | ||
1312 | int want_write = (mode & O_ACCMODE) != O_RDONLY; | 1323 | int want_write = (mode & O_ACCMODE) != O_RDONLY; |
1313 | 1324 | ||
1314 | new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK); | 1325 | new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK); |
1315 | if (IS_ERR(new_fl)) | 1326 | if (IS_ERR(new_fl)) |
1316 | return PTR_ERR(new_fl); | 1327 | return PTR_ERR(new_fl); |
1328 | new_fl->fl_flags = type; | ||
1317 | 1329 | ||
1318 | spin_lock(&inode->i_lock); | 1330 | spin_lock(&inode->i_lock); |
1319 | 1331 | ||
@@ -1323,13 +1335,16 @@ int __break_lease(struct inode *inode, unsigned int mode) | |||
1323 | if ((flock == NULL) || !IS_LEASE(flock)) | 1335 | if ((flock == NULL) || !IS_LEASE(flock)) |
1324 | goto out; | 1336 | goto out; |
1325 | 1337 | ||
1326 | if (!locks_conflict(flock, new_fl)) | 1338 | for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { |
1339 | if (leases_conflict(fl, new_fl)) { | ||
1340 | lease_conflict = true; | ||
1341 | if (fl->fl_owner == current->files) | ||
1342 | i_have_this_lease = 1; | ||
1343 | } | ||
1344 | } | ||
1345 | if (!lease_conflict) | ||
1327 | goto out; | 1346 | goto out; |
1328 | 1347 | ||
1329 | for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) | ||
1330 | if (fl->fl_owner == current->files) | ||
1331 | i_have_this_lease = 1; | ||
1332 | |||
1333 | break_time = 0; | 1348 | break_time = 0; |
1334 | if (lease_break_time > 0) { | 1349 | if (lease_break_time > 0) { |
1335 | break_time = jiffies + lease_break_time * HZ; | 1350 | break_time = jiffies + lease_break_time * HZ; |
@@ -1338,6 +1353,8 @@ int __break_lease(struct inode *inode, unsigned int mode) | |||
1338 | } | 1353 | } |
1339 | 1354 | ||
1340 | for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { | 1355 | for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { |
1356 | if (!leases_conflict(fl, new_fl)) | ||
1357 | continue; | ||
1341 | if (want_write) { | 1358 | if (want_write) { |
1342 | if (fl->fl_flags & FL_UNLOCK_PENDING) | 1359 | if (fl->fl_flags & FL_UNLOCK_PENDING) |
1343 | continue; | 1360 | continue; |
@@ -1379,7 +1396,7 @@ restart: | |||
1379 | */ | 1396 | */ |
1380 | for (flock = inode->i_flock; flock && IS_LEASE(flock); | 1397 | for (flock = inode->i_flock; flock && IS_LEASE(flock); |
1381 | flock = flock->fl_next) { | 1398 | flock = flock->fl_next) { |
1382 | if (locks_conflict(new_fl, flock)) | 1399 | if (leases_conflict(new_fl, flock)) |
1383 | goto restart; | 1400 | goto restart; |
1384 | } | 1401 | } |
1385 | error = 0; | 1402 | error = 0; |
@@ -1460,9 +1477,26 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp | |||
1460 | struct file_lock *fl, **before, **my_before = NULL, *lease; | 1477 | struct file_lock *fl, **before, **my_before = NULL, *lease; |
1461 | struct dentry *dentry = filp->f_path.dentry; | 1478 | struct dentry *dentry = filp->f_path.dentry; |
1462 | struct inode *inode = dentry->d_inode; | 1479 | struct inode *inode = dentry->d_inode; |
1480 | bool is_deleg = (*flp)->fl_flags & FL_DELEG; | ||
1463 | int error; | 1481 | int error; |
1464 | 1482 | ||
1465 | lease = *flp; | 1483 | lease = *flp; |
1484 | /* | ||
1485 | * In the delegation case we need mutual exclusion with | ||
1486 | * a number of operations that take the i_mutex. We trylock | ||
1487 | * because delegations are an optional optimization, and if | ||
1488 | * there's some chance of a conflict--we'd rather not | ||
1489 | * bother, maybe that's a sign this just isn't a good file to | ||
1490 | * hand out a delegation on. | ||
1491 | */ | ||
1492 | if (is_deleg && !mutex_trylock(&inode->i_mutex)) | ||
1493 | return -EAGAIN; | ||
1494 | |||
1495 | if (is_deleg && arg == F_WRLCK) { | ||
1496 | /* Write delegations are not currently supported: */ | ||
1497 | WARN_ON_ONCE(1); | ||
1498 | return -EINVAL; | ||
1499 | } | ||
1466 | 1500 | ||
1467 | error = -EAGAIN; | 1501 | error = -EAGAIN; |
1468 | if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) | 1502 | if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) |
@@ -1514,9 +1548,10 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp | |||
1514 | goto out; | 1548 | goto out; |
1515 | 1549 | ||
1516 | locks_insert_lock(before, lease); | 1550 | locks_insert_lock(before, lease); |
1517 | return 0; | 1551 | error = 0; |
1518 | |||
1519 | out: | 1552 | out: |
1553 | if (is_deleg) | ||
1554 | mutex_unlock(&inode->i_mutex); | ||
1520 | return error; | 1555 | return error; |
1521 | } | 1556 | } |
1522 | 1557 | ||