Commit f61ec2c9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'afs-fixes-20171124' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull AFS fixes from David Howells:

 - Make AFS file locking work again.

 - Don't write to a page that's being written out, but wait for it to
   complete.

 - Do d_drop() and d_add() in the right places.

 - Put keys on error paths.

 - Remove some redundant code.

* tag 'afs-fixes-20171124' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: remove redundant assignment of dvnode to itself
  afs: cell: Remove unnecessary code in afs_lookup_cell
  afs: Fix signal handling in some file ops
  afs: Fix some dentry handling in dir ops and missing key_puts
  afs: Make afs_write_begin() avoid writing to a page that's being stored
  afs: Fix file locking
parents 7753ea09 43dd388b
...@@ -207,13 +207,8 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, ...@@ -207,13 +207,8 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net,
rcu_read_lock(); rcu_read_lock();
cell = afs_lookup_cell_rcu(net, name, namesz); cell = afs_lookup_cell_rcu(net, name, namesz);
rcu_read_unlock(); rcu_read_unlock();
if (!IS_ERR(cell)) { if (!IS_ERR(cell))
if (excl) {
afs_put_cell(net, cell);
return ERR_PTR(-EEXIST);
}
goto wait_for_cell; goto wait_for_cell;
}
} }
/* Assume we're probably going to create a cell and preallocate and /* Assume we're probably going to create a cell and preallocate and
......
...@@ -765,6 +765,8 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc, ...@@ -765,6 +765,8 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
if (fc->ac.error < 0) if (fc->ac.error < 0)
return; return;
d_drop(new_dentry);
inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key, inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key,
newfid, newstatus, newcb, fc->cbi); newfid, newstatus, newcb, fc->cbi);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
...@@ -775,9 +777,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc, ...@@ -775,9 +777,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
return; return;
} }
d_instantiate(new_dentry, inode); d_add(new_dentry, inode);
if (d_unhashed(new_dentry))
d_rehash(new_dentry);
} }
/* /*
...@@ -818,6 +818,8 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -818,6 +818,8 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret < 0) if (ret < 0)
goto error_key; goto error_key;
} else {
goto error_key;
} }
key_put(key); key_put(key);
...@@ -972,7 +974,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -972,7 +974,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
struct afs_fs_cursor fc; struct afs_fs_cursor fc;
struct afs_file_status newstatus; struct afs_file_status newstatus;
struct afs_callback newcb; struct afs_callback newcb;
struct afs_vnode *dvnode = dvnode = AFS_FS_I(dir); struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_fid newfid; struct afs_fid newfid;
struct key *key; struct key *key;
int ret; int ret;
...@@ -1006,6 +1008,8 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -1006,6 +1008,8 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret < 0) if (ret < 0)
goto error_key; goto error_key;
} else {
goto error_key;
} }
key_put(key); key_put(key);
...@@ -1053,7 +1057,7 @@ static int afs_link(struct dentry *from, struct inode *dir, ...@@ -1053,7 +1057,7 @@ static int afs_link(struct dentry *from, struct inode *dir,
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
if (mutex_lock_interruptible_nested(&vnode->io_lock, 1) < 0) { if (mutex_lock_interruptible_nested(&vnode->io_lock, 1) < 0) {
afs_end_vnode_operation(&fc); afs_end_vnode_operation(&fc);
return -ERESTARTSYS; goto error_key;
} }
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
...@@ -1071,6 +1075,8 @@ static int afs_link(struct dentry *from, struct inode *dir, ...@@ -1071,6 +1075,8 @@ static int afs_link(struct dentry *from, struct inode *dir,
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret < 0) if (ret < 0)
goto error_key; goto error_key;
} else {
goto error_key;
} }
key_put(key); key_put(key);
...@@ -1130,6 +1136,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -1130,6 +1136,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret < 0) if (ret < 0)
goto error_key; goto error_key;
} else {
goto error_key;
} }
key_put(key); key_put(key);
...@@ -1180,7 +1188,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1180,7 +1188,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (orig_dvnode != new_dvnode) { if (orig_dvnode != new_dvnode) {
if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) { if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) {
afs_end_vnode_operation(&fc); afs_end_vnode_operation(&fc);
return -ERESTARTSYS; goto error_key;
} }
} }
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
...@@ -1199,14 +1207,9 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1199,14 +1207,9 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto error_key; goto error_key;
} }
key_put(key);
_leave(" = 0");
return 0;
error_key: error_key:
key_put(key); key_put(key);
error: error:
d_drop(new_dentry);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
...@@ -170,7 +170,7 @@ void afs_lock_work(struct work_struct *work) ...@@ -170,7 +170,7 @@ void afs_lock_work(struct work_struct *work)
{ {
struct afs_vnode *vnode = struct afs_vnode *vnode =
container_of(work, struct afs_vnode, lock_work.work); container_of(work, struct afs_vnode, lock_work.work);
struct file_lock *fl; struct file_lock *fl, *next;
afs_lock_type_t type; afs_lock_type_t type;
struct key *key; struct key *key;
int ret; int ret;
...@@ -179,117 +179,136 @@ void afs_lock_work(struct work_struct *work) ...@@ -179,117 +179,136 @@ void afs_lock_work(struct work_struct *work)
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
if (test_bit(AFS_VNODE_UNLOCKING, &vnode->flags)) { again:
_debug("wstate %u for %p", vnode->lock_state, vnode);
switch (vnode->lock_state) {
case AFS_VNODE_LOCK_NEED_UNLOCK:
_debug("unlock"); _debug("unlock");
vnode->lock_state = AFS_VNODE_LOCK_UNLOCKING;
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
/* attempt to release the server lock; if it fails, we just /* attempt to release the server lock; if it fails, we just
* wait 5 minutes and it'll time out anyway */ * wait 5 minutes and it'll expire anyway */
ret = afs_release_lock(vnode, vnode->unlock_key); ret = afs_release_lock(vnode, vnode->lock_key);
if (ret < 0) if (ret < 0)
printk(KERN_WARNING "AFS:" printk(KERN_WARNING "AFS:"
" Failed to release lock on {%x:%x} error %d\n", " Failed to release lock on {%x:%x} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret); vnode->fid.vid, vnode->fid.vnode, ret);
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
key_put(vnode->unlock_key); key_put(vnode->lock_key);
vnode->unlock_key = NULL; vnode->lock_key = NULL;
clear_bit(AFS_VNODE_UNLOCKING, &vnode->flags); vnode->lock_state = AFS_VNODE_LOCK_NONE;
}
if (list_empty(&vnode->pending_locks)) {
spin_unlock(&vnode->lock);
return;
}
/* The new front of the queue now owns the state variables. */
next = list_entry(vnode->pending_locks.next,
struct file_lock, fl_u.afs.link);
vnode->lock_key = afs_file_key(next->fl_file);
vnode->lock_type = (next->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
goto again;
/* if we've got a lock, then it must be time to extend that lock as AFS /* If we've already got a lock, then it must be time to extend that
* locks time out after 5 minutes */ * lock as AFS locks time out after 5 minutes.
if (!list_empty(&vnode->granted_locks)) { */
case AFS_VNODE_LOCK_GRANTED:
_debug("extend"); _debug("extend");
if (test_and_set_bit(AFS_VNODE_LOCKING, &vnode->flags)) ASSERT(!list_empty(&vnode->granted_locks));
BUG();
fl = list_entry(vnode->granted_locks.next, key = key_get(vnode->lock_key);
struct file_lock, fl_u.afs.link); vnode->lock_state = AFS_VNODE_LOCK_EXTENDING;
key = key_get(afs_file_key(fl->fl_file));
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
ret = afs_extend_lock(vnode, key); ret = afs_extend_lock(vnode, key); /* RPC */
clear_bit(AFS_VNODE_LOCKING, &vnode->flags);
key_put(key); key_put(key);
switch (ret) {
case 0: if (ret < 0)
pr_warning("AFS: Failed to extend lock on {%x:%x} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret);
spin_lock(&vnode->lock);
if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING)
goto again;
vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
if (ret == 0)
afs_schedule_lock_extension(vnode); afs_schedule_lock_extension(vnode);
break; else
default:
/* ummm... we failed to extend the lock - retry
* extension shortly */
printk(KERN_WARNING "AFS:"
" Failed to extend lock on {%x:%x} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret);
queue_delayed_work(afs_lock_manager, &vnode->lock_work, queue_delayed_work(afs_lock_manager, &vnode->lock_work,
HZ * 10); HZ * 10);
break; spin_unlock(&vnode->lock);
} _leave(" [ext]");
_leave(" [extend]");
return; return;
}
/* if we don't have a granted lock, then we must've been called back by /* If we don't have a granted lock, then we must've been called
* the server, and so if might be possible to get a lock we're * back by the server, and so if might be possible to get a
* currently waiting for */ * lock we're currently waiting for.
if (!list_empty(&vnode->pending_locks)) { */
case AFS_VNODE_LOCK_WAITING_FOR_CB:
_debug("get"); _debug("get");
if (test_and_set_bit(AFS_VNODE_LOCKING, &vnode->flags)) key = key_get(vnode->lock_key);
BUG(); type = vnode->lock_type;
fl = list_entry(vnode->pending_locks.next, vnode->lock_state = AFS_VNODE_LOCK_SETTING;
struct file_lock, fl_u.afs.link);
key = key_get(afs_file_key(fl->fl_file));
type = (fl->fl_type == F_RDLCK) ?
AFS_LOCK_READ : AFS_LOCK_WRITE;
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
ret = afs_set_lock(vnode, key, type); ret = afs_set_lock(vnode, key, type); /* RPC */
clear_bit(AFS_VNODE_LOCKING, &vnode->flags); key_put(key);
spin_lock(&vnode->lock);
switch (ret) { switch (ret) {
case -EWOULDBLOCK: case -EWOULDBLOCK:
_debug("blocked"); _debug("blocked");
break; break;
case 0: case 0:
_debug("acquired"); _debug("acquired");
if (type == AFS_LOCK_READ) vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
set_bit(AFS_VNODE_READLOCKED, &vnode->flags); /* Fall through */
else
set_bit(AFS_VNODE_WRITELOCKED, &vnode->flags);
ret = AFS_LOCK_GRANTED;
default: default:
spin_lock(&vnode->lock); /* Pass the lock or the error onto the first locker in
/* the pending lock may have been withdrawn due to a * the list - if they're looking for this type of lock.
* signal */ * If they're not, we assume that whoever asked for it
if (list_entry(vnode->pending_locks.next, * took a signal.
struct file_lock, fl_u.afs.link) == fl) { */
fl->fl_u.afs.state = ret; if (list_empty(&vnode->pending_locks)) {
if (ret == AFS_LOCK_GRANTED)
afs_grant_locks(vnode, fl);
else
list_del_init(&fl->fl_u.afs.link);
wake_up(&fl->fl_wait);
spin_unlock(&vnode->lock);
} else {
_debug("withdrawn"); _debug("withdrawn");
clear_bit(AFS_VNODE_READLOCKED, &vnode->flags); vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags); goto again;
spin_unlock(&vnode->lock);
afs_release_lock(vnode, key);
if (!list_empty(&vnode->pending_locks))
afs_lock_may_be_available(vnode);
} }
break;
fl = list_entry(vnode->pending_locks.next,
struct file_lock, fl_u.afs.link);
type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
if (vnode->lock_type != type) {
_debug("changed");
vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
goto again;
}
fl->fl_u.afs.state = ret;
if (ret == 0)
afs_grant_locks(vnode, fl);
else
list_del_init(&fl->fl_u.afs.link);
wake_up(&fl->fl_wait);
spin_unlock(&vnode->lock);
_leave(" [granted]");
return;
} }
key_put(key);
_leave(" [pend]"); default:
/* Looks like a lock request was withdrawn. */
spin_unlock(&vnode->lock);
_leave(" [no]");
return; return;
} }
/* looks like the lock request was withdrawn on a signal */
spin_unlock(&vnode->lock);
_leave(" [no locks]");
} }
/* /*
...@@ -298,15 +317,105 @@ void afs_lock_work(struct work_struct *work) ...@@ -298,15 +317,105 @@ void afs_lock_work(struct work_struct *work)
* AF_RXRPC * AF_RXRPC
* - the caller must hold the vnode lock * - the caller must hold the vnode lock
*/ */
static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key) static void afs_defer_unlock(struct afs_vnode *vnode)
{ {
cancel_delayed_work(&vnode->lock_work); _enter("");
if (!test_and_clear_bit(AFS_VNODE_READLOCKED, &vnode->flags) &&
!test_and_clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags)) if (vnode->lock_state == AFS_VNODE_LOCK_GRANTED ||
BUG(); vnode->lock_state == AFS_VNODE_LOCK_EXTENDING) {
if (test_and_set_bit(AFS_VNODE_UNLOCKING, &vnode->flags)) cancel_delayed_work(&vnode->lock_work);
BUG();
vnode->unlock_key = key_get(key); vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
afs_lock_may_be_available(vnode);
}
}
/*
* Check that our view of the file metadata is up to date and check to see
* whether we think that we have a locking permit.
*/
static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key,
afs_lock_type_t type, bool can_sleep)
{
afs_access_t access;
int ret;
/* Make sure we've got a callback on this file and that our view of the
* data version is up to date.
*/
ret = afs_validate(vnode, key);
if (ret < 0)
return ret;
/* Check the permission set to see if we're actually going to be
* allowed to get a lock on this file.
*/
ret = afs_check_permit(vnode, key, &access);
if (ret < 0)
return ret;
/* At a rough estimation, you need LOCK, WRITE or INSERT perm to
* read-lock a file and WRITE or INSERT perm to write-lock a file.
*
* We can't rely on the server to do this for us since if we want to
* share a read lock that we already have, we won't go the server.
*/
if (type == AFS_LOCK_READ) {
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK)))
return -EACCES;
if (vnode->status.lock_count == -1 && !can_sleep)
return -EAGAIN; /* Write locked */
} else {
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE)))
return -EACCES;
if (vnode->status.lock_count != 0 && !can_sleep)
return -EAGAIN; /* Locked */
}
return 0;
}
/*
* Remove the front runner from the pending queue.
* - The caller must hold vnode->lock.
*/
static void afs_dequeue_lock(struct afs_vnode *vnode, struct file_lock *fl)
{
struct file_lock *next;
_enter("");
/* ->lock_type, ->lock_key and ->lock_state only belong to this
* file_lock if we're at the front of the pending queue or if we have
* the lock granted or if the lock_state is NEED_UNLOCK or UNLOCKING.
*/
if (vnode->granted_locks.next == &fl->fl_u.afs.link &&
vnode->granted_locks.prev == &fl->fl_u.afs.link) {
list_del_init(&fl->fl_u.afs.link);
afs_defer_unlock(vnode);
return;
}
if (!list_empty(&vnode->granted_locks) ||
vnode->pending_locks.next != &fl->fl_u.afs.link) {
list_del_init(&fl->fl_u.afs.link);
return;
}
list_del_init(&fl->fl_u.afs.link);
key_put(vnode->lock_key);
vnode->lock_key = NULL;
vnode->lock_state = AFS_VNODE_LOCK_NONE;
if (list_empty(&vnode->pending_locks))
return;
/* The new front of the queue now owns the state variables. */
next = list_entry(vnode->pending_locks.next,
struct file_lock, fl_u.afs.link);
vnode->lock_key = afs_file_key(next->fl_file);
vnode->lock_type = (next->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
afs_lock_may_be_available(vnode); afs_lock_may_be_available(vnode);
} }
...@@ -315,7 +424,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key) ...@@ -315,7 +424,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key)
*/ */
static int afs_do_setlk(struct file *file, struct file_lock *fl) static int afs_do_setlk(struct file *file, struct file_lock *fl)
{ {
struct inode *inode = file_inode(file); struct inode *inode = locks_inode(file);
struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_vnode *vnode = AFS_FS_I(inode);
afs_lock_type_t type; afs_lock_type_t type;
struct key *key = afs_file_key(file); struct key *key = afs_file_key(file);
...@@ -333,165 +442,136 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) ...@@ -333,165 +442,136 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
spin_lock(&inode->i_lock); ret = afs_do_setlk_check(vnode, key, type, fl->fl_flags & FL_SLEEP);
/* make sure we've got a callback on this file and that our view of the
* data version is up to date */
ret = afs_validate(vnode, key);
if (ret < 0) if (ret < 0)
goto error; return ret;
if (vnode->status.lock_count != 0 && !(fl->fl_flags & FL_SLEEP)) {
ret = -EAGAIN;
goto error;
}
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
/* if we've already got a readlock on the server then we can instantly /* If we've already got a readlock on the server then we instantly
* grant another readlock, irrespective of whether there are any * grant another readlock, irrespective of whether there are any
* pending writelocks */ * pending writelocks.
*/
if (type == AFS_LOCK_READ && if (type == AFS_LOCK_READ &&
vnode->flags & (1 << AFS_VNODE_READLOCKED)) { vnode->lock_state == AFS_VNODE_LOCK_GRANTED &&
vnode->lock_type == AFS_LOCK_READ) {
_debug("instant readlock"); _debug("instant readlock");
ASSERTCMP(vnode->flags &
((1 << AFS_VNODE_LOCKING) |
(1 << AFS_VNODE_WRITELOCKED)), ==, 0);
ASSERT(!list_empty(&vnode->granted_locks)); ASSERT(!list_empty(&vnode->granted_locks));
goto sharing_existing_lock; goto share_existing_lock;
} }
/* if there's no-one else with a lock on this vnode, then we need to list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);
* ask the server for a lock */
if (list_empty(&vnode->pending_locks) &&
list_empty(&vnode->granted_locks)) {
_debug("not locked");
ASSERTCMP(vnode->flags &
((1 << AFS_VNODE_LOCKING) |
(1 << AFS_VNODE_READLOCKED) |
(1 << AFS_VNODE_WRITELOCKED)), ==, 0);
list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);
set_bit(AFS_VNODE_LOCKING, &vnode->flags);
spin_unlock(&vnode->lock);
ret = afs_set_lock(vnode, key, type); if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
clear_bit(AFS_VNODE_LOCKING, &vnode->flags); goto need_to_wait;
switch (ret) {
case 0:
_debug("acquired");
goto acquired_server_lock;
case -EWOULDBLOCK:
_debug("would block");
spin_lock(&vnode->lock);
ASSERT(list_empty(&vnode->granted_locks));
ASSERTCMP(vnode->pending_locks.next, ==,
&fl->fl_u.afs.link);
goto wait;
default:
spin_lock(&vnode->lock);
list_del_init(&fl->fl_u.afs.link);
spin_unlock(&vnode->lock);
goto error;
}
}
/* otherwise, we need to wait for a local lock to become available */ /* We don't have a lock on this vnode and we aren't currently waiting
_debug("wait local"); * for one either, so ask the server for a lock.
list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); *
wait: * Note that we need to be careful if we get interrupted by a signal
if (!(fl->fl_flags & FL_SLEEP)) { * after dispatching the request as we may still get the lock, even
_debug("noblock"); * though we don't wait for the reply (it's not too bad a problem - the
ret = -EAGAIN; * lock will expire in 10 mins anyway).
goto abort_attempt; */
} _debug("not locked");
vnode->lock_key = key_get(key);
vnode->lock_type = type;
vnode->lock_state = AFS_VNODE_LOCK_SETTING;
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
/* now we need to sleep and wait for the lock manager thread to get the ret = afs_set_lock(vnode, key, type); /* RPC */
* lock from the server */
_debug("sleep");
ret = wait_event_interruptible(fl->fl_wait,
fl->fl_u.afs.state <= AFS_LOCK_GRANTED);
if (fl->fl_u.afs.state <= AFS_LOCK_GRANTED) {
ret = fl->fl_u.afs.state;
if (ret < 0)
goto error;
spin_lock(&vnode->lock);
goto given_lock;
}
/* we were interrupted, but someone may still be in the throes of
* giving us the lock */
_debug("intr");
ASSERTCMP(ret, ==, -ERESTARTSYS);
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
if (fl->fl_u.afs.state <= AFS_LOCK_GRANTED) { switch (ret) {
ret = fl->fl_u.afs.state; default:
if (ret < 0) { goto abort_attempt;
spin_unlock(&vnode->lock);
goto error;
}
goto given_lock;
}
abort_attempt: case -EWOULDBLOCK:
/* we aren't going to get the lock, either because we're unwilling to /* The server doesn't have a lock-waiting queue, so the client
* wait, or because some signal happened */ * will have to retry. The server will break the outstanding
_debug("abort"); * callbacks on a file when a lock is released.
if (list_empty(&vnode->granted_locks) && */
vnode->pending_locks.next == &fl->fl_u.afs.link) { _debug("would block");
if (vnode->pending_locks.prev != &fl->fl_u.afs.link) { ASSERT(list_empty(&vnode->granted_locks));
/* kick the next pending lock into having a go */ ASSERTCMP(vnode->pending_locks.next, ==, &fl->fl_u.afs.link);
list_del_init(&fl->fl_u.afs.link); vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
afs_lock_may_be_available(vnode); goto need_to_wait;
}
} else { case 0:
list_del_init(&fl->fl_u.afs.link); _debug("acquired");
break;
} }
spin_unlock(&vnode->lock);
goto error;
acquired_server_lock:
/* we've acquired a server lock, but it needs to be renewed after 5 /* we've acquired a server lock, but it needs to be renewed after 5
* mins */ * mins */
spin_lock(&vnode->lock); vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
afs_schedule_lock_extension(vnode); afs_schedule_lock_extension(vnode);
if (type == AFS_LOCK_READ)
set_bit(AFS_VNODE_READLOCKED, &vnode->flags); share_existing_lock:
else
set_bit(AFS_VNODE_WRITELOCKED, &vnode->flags);
sharing_existing_lock:
/* the lock has been granted as far as we're concerned... */ /* the lock has been granted as far as we're concerned... */
fl->fl_u.afs.state = AFS_LOCK_GRANTED; fl->fl_u.afs.state = AFS_LOCK_GRANTED;
list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks);
given_lock: given_lock:
/* ... but we do still need to get the VFS's blessing */ /* ... but we do still need to get the VFS's blessing */
ASSERT(!(vnode->flags & (1 << AFS_VNODE_LOCKING))); spin_unlock(&vnode->lock);
ASSERT((vnode->flags & ((1 << AFS_VNODE_READLOCKED) |
(1 << AFS_VNODE_WRITELOCKED))) != 0);
ret = posix_lock_file(file, fl, NULL); ret = posix_lock_file(file, fl, NULL);
if (ret < 0) if (ret < 0)
goto vfs_rejected_lock; goto vfs_rejected_lock;
spin_unlock(&vnode->lock);
/* again, make sure we've got a callback on this file and, again, make /* Again, make sure we've got a callback on this file and, again, make
* sure that our view of the data version is up to date (we ignore * sure that our view of the data version is up to date (we ignore
* errors incurred here and deal with the consequences elsewhere) */ * errors incurred here and deal with the consequences elsewhere).
*/
afs_validate(vnode, key); afs_validate(vnode, key);
_leave(" = 0");
return 0;
error: need_to_wait:
spin_unlock(&inode->i_lock); /* We're going to have to wait. Either this client doesn't have a lock
* on the server yet and we need to wait for a callback to occur, or
* the client does have a lock on the server, but it belongs to some
* other process(es) and is incompatible with the lock we want.
*/
ret = -EAGAIN;
if (fl->fl_flags & FL_SLEEP) {
spin_unlock(&vnode->lock);
_debug("sleep");
ret = wait_event_interruptible(fl->fl_wait,
fl->fl_u.afs.state != AFS_LOCK_PENDING);
spin_lock(&vnode->lock);
}
if (fl->fl_u.afs.state == AFS_LOCK_GRANTED)
goto given_lock;
if (fl->fl_u.afs.state < 0)
ret = fl->fl_u.afs.state;
abort_attempt:
/* we aren't going to get the lock, either because we're unwilling to
* wait, or because some signal happened */
_debug("abort");
afs_dequeue_lock(vnode, fl);
error_unlock:
spin_unlock(&vnode->lock);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
vfs_rejected_lock: vfs_rejected_lock:
/* the VFS rejected the lock we just obtained, so we have to discard /* The VFS rejected the lock we just obtained, so we have to discard
* what we just got */ * what we just got. We defer this to the lock manager work item to
* deal with.
*/
_debug("vfs refused %d", ret); _debug("vfs refused %d", ret);
spin_lock(&vnode->lock);
list_del_init(&fl->fl_u.afs.link); list_del_init(&fl->fl_u.afs.link);
if (list_empty(&vnode->granted_locks)) if (list_empty(&vnode->granted_locks))
afs_defer_unlock(vnode, key); afs_defer_unlock(vnode);
goto abort_attempt; goto error_unlock;
} }
/* /*
...@@ -499,34 +579,21 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) ...@@ -499,34 +579,21 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
*/ */
static int afs_do_unlk(struct file *file, struct file_lock *fl) static int afs_do_unlk(struct file *file, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
struct key *key = afs_file_key(file);
int ret; int ret;
_enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type);
/* Flush all pending writes before doing anything with locks. */
vfs_fsync(file, 0);
/* only whole-file unlocks are supported */ /* only whole-file unlocks are supported */
if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX) if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX)
return -EINVAL; return -EINVAL;
fl->fl_ops = &afs_lock_ops;
INIT_LIST_HEAD(&fl->fl_u.afs.link);
fl->fl_u.afs.state = AFS_LOCK_PENDING;
spin_lock(&vnode->lock);
ret = posix_lock_file(file, fl, NULL); ret = posix_lock_file(file, fl, NULL);
if (ret < 0) { _leave(" = %d [%u]", ret, vnode->lock_state);
spin_unlock(&vnode->lock); return ret;
_leave(" = %d [vfs]", ret);
return ret;
}
/* discard the server lock only if all granted locks are gone */
if (list_empty(&vnode->granted_locks))
afs_defer_unlock(vnode, key);
spin_unlock(&vnode->lock);
_leave(" = 0");
return 0;
} }
/* /*
...@@ -534,7 +601,7 @@ static int afs_do_unlk(struct file *file, struct file_lock *fl) ...@@ -534,7 +601,7 @@ static int afs_do_unlk(struct file *file, struct file_lock *fl)
*/ */
static int afs_do_getlk(struct file *file, struct file_lock *fl) static int afs_do_getlk(struct file *file, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
struct key *key = afs_file_key(file); struct key *key = afs_file_key(file);
int ret, lock_count; int ret, lock_count;
...@@ -542,29 +609,25 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl) ...@@ -542,29 +609,25 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
fl->fl_type = F_UNLCK; fl->fl_type = F_UNLCK;
inode_lock(&vnode->vfs_inode);
/* check local lock records first */ /* check local lock records first */
ret = 0;
posix_test_lock(file, fl); posix_test_lock(file, fl);
if (fl->fl_type == F_UNLCK) { if (fl->fl_type == F_UNLCK) {
/* no local locks; consult the server */ /* no local locks; consult the server */
ret = afs_fetch_status(vnode, key); ret = afs_fetch_status(vnode, key);
if (ret < 0) if (ret < 0)
goto error; goto error;
lock_count = vnode->status.lock_count;
if (lock_count) { lock_count = READ_ONCE(vnode->status.lock_count);
if (lock_count > 0) if (lock_count > 0)
fl->fl_type = F_RDLCK; fl->fl_type = F_RDLCK;
else else
fl->fl_type = F_WRLCK; fl->fl_type = F_WRLCK;
fl->fl_start = 0; fl->fl_start = 0;
fl->fl_end = OFFSET_MAX; fl->fl_end = OFFSET_MAX;
}
} }
ret = 0;
error: error:
inode_unlock(&vnode->vfs_inode);
_leave(" = %d [%hd]", ret, fl->fl_type); _leave(" = %d [%hd]", ret, fl->fl_type);
return ret; return ret;
} }
...@@ -574,7 +637,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl) ...@@ -574,7 +637,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
*/ */
int afs_lock(struct file *file, int cmd, struct file_lock *fl) int afs_lock(struct file *file, int cmd, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
_enter("{%x:%u},%d,{t=%x,fl=%x,r=%Ld:%Ld}", _enter("{%x:%u},%d,{t=%x,fl=%x,r=%Ld:%Ld}",
vnode->fid.vid, vnode->fid.vnode, cmd, vnode->fid.vid, vnode->fid.vnode, cmd,
...@@ -597,7 +660,7 @@ int afs_lock(struct file *file, int cmd, struct file_lock *fl) ...@@ -597,7 +660,7 @@ int afs_lock(struct file *file, int cmd, struct file_lock *fl)
*/ */
int afs_flock(struct file *file, int cmd, struct file_lock *fl) int afs_flock(struct file *file, int cmd, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
_enter("{%x:%u},%d,{t=%x,fl=%x}", _enter("{%x:%u},%d,{t=%x,fl=%x}",
vnode->fid.vid, vnode->fid.vnode, cmd, vnode->fid.vid, vnode->fid.vnode, cmd,
...@@ -627,9 +690,13 @@ int afs_flock(struct file *file, int cmd, struct file_lock *fl) ...@@ -627,9 +690,13 @@ int afs_flock(struct file *file, int cmd, struct file_lock *fl)
*/ */
static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl) static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(locks_inode(fl->fl_file));
_enter(""); _enter("");
spin_lock(&vnode->lock);
list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link); list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link);
spin_unlock(&vnode->lock);
} }
/* /*
...@@ -638,7 +705,12 @@ static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl) ...@@ -638,7 +705,12 @@ static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl)
*/ */
static void afs_fl_release_private(struct file_lock *fl) static void afs_fl_release_private(struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(locks_inode(fl->fl_file));
_enter(""); _enter("");
list_del_init(&fl->fl_u.afs.link); spin_lock(&vnode->lock);
afs_dequeue_lock(vnode, fl);
_debug("state %u for %p", vnode->lock_state, vnode);
spin_unlock(&vnode->lock);
} }
...@@ -430,6 +430,16 @@ struct afs_volume { ...@@ -430,6 +430,16 @@ struct afs_volume {
u8 name[AFS_MAXVOLNAME + 1]; /* NUL-padded volume name */ u8 name[AFS_MAXVOLNAME + 1]; /* NUL-padded volume name */
}; };
enum afs_lock_state {
AFS_VNODE_LOCK_NONE, /* The vnode has no lock on the server */
AFS_VNODE_LOCK_WAITING_FOR_CB, /* We're waiting for the server to break the callback */
AFS_VNODE_LOCK_SETTING, /* We're asking the server for a lock */
AFS_VNODE_LOCK_GRANTED, /* We have a lock on the server */
AFS_VNODE_LOCK_EXTENDING, /* We're extending a lock on the server */
AFS_VNODE_LOCK_NEED_UNLOCK, /* We need to unlock on the server */
AFS_VNODE_LOCK_UNLOCKING, /* We're telling the server to unlock */
};
/* /*
* AFS inode private data * AFS inode private data
*/ */
...@@ -454,18 +464,16 @@ struct afs_vnode { ...@@ -454,18 +464,16 @@ struct afs_vnode {
#define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */ #define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */
#define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */ #define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */
#define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ #define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */
#define AFS_VNODE_LOCKING 6 /* set if waiting for lock on vnode */ #define AFS_VNODE_AUTOCELL 6 /* set if Vnode is an auto mount point */
#define AFS_VNODE_READLOCKED 7 /* set if vnode is read-locked on the server */ #define AFS_VNODE_PSEUDODIR 7 /* set if Vnode is a pseudo directory */
#define AFS_VNODE_WRITELOCKED 8 /* set if vnode is write-locked on the server */
#define AFS_VNODE_UNLOCKING 9 /* set if vnode is being unlocked on the server */
#define AFS_VNODE_AUTOCELL 10 /* set if Vnode is an auto mount point */
#define AFS_VNODE_PSEUDODIR 11 /* set if Vnode is a pseudo directory */
struct list_head wb_keys; /* List of keys available for writeback */ struct list_head wb_keys; /* List of keys available for writeback */
struct list_head pending_locks; /* locks waiting to be granted */ struct list_head pending_locks; /* locks waiting to be granted */
struct list_head granted_locks; /* locks granted on this file */ struct list_head granted_locks; /* locks granted on this file */
struct delayed_work lock_work; /* work to be done in locking */ struct delayed_work lock_work; /* work to be done in locking */
struct key *unlock_key; /* key to be used in unlocking */ struct key *lock_key; /* Key to be used in lock ops */
enum afs_lock_state lock_state : 8;
afs_lock_type_t lock_type : 8;
/* outstanding callback notification on this file */ /* outstanding callback notification on this file */
struct afs_cb_interest *cb_interest; /* Server on which this resides */ struct afs_cb_interest *cb_interest; /* Server on which this resides */
...@@ -843,6 +851,7 @@ extern void afs_clear_permits(struct afs_vnode *); ...@@ -843,6 +851,7 @@ extern void afs_clear_permits(struct afs_vnode *);
extern void afs_cache_permit(struct afs_vnode *, struct key *, unsigned int); extern void afs_cache_permit(struct afs_vnode *, struct key *, unsigned int);
extern void afs_zap_permits(struct rcu_head *); extern void afs_zap_permits(struct rcu_head *);
extern struct key *afs_request_key(struct afs_cell *); extern struct key *afs_request_key(struct afs_cell *);
extern int afs_check_permit(struct afs_vnode *, struct key *, afs_access_t *);
extern int afs_permission(struct inode *, int); extern int afs_permission(struct inode *, int);
extern void __exit afs_clean_up_permit_cache(void); extern void __exit afs_clean_up_permit_cache(void);
......
...@@ -46,8 +46,7 @@ bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode ...@@ -46,8 +46,7 @@ bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode
return false; return false;
} }
if (test_bit(AFS_VNODE_READLOCKED, &vnode->flags) || if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
test_bit(AFS_VNODE_WRITELOCKED, &vnode->flags))
fc->flags |= AFS_FS_CURSOR_CUR_ONLY; fc->flags |= AFS_FS_CURSOR_CUR_ONLY;
return true; return true;
} }
...@@ -117,7 +116,7 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code) ...@@ -117,7 +116,7 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code)
case VSALVAGING: m = "being salvaged"; break; case VSALVAGING: m = "being salvaged"; break;
default: m = "busy"; break; default: m = "busy"; break;
} }
pr_notice("kAFS: Volume %u '%s' is %s\n", volume->vid, volume->name, m); pr_notice("kAFS: Volume %u '%s' is %s\n", volume->vid, volume->name, m);
} }
...@@ -438,24 +437,67 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc) ...@@ -438,24 +437,67 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
_enter(""); _enter("");
if (!cbi) { switch (fc->ac.error) {
fc->ac.error = -ESTALE; case SHRT_MAX:
if (!cbi) {
fc->ac.error = -ESTALE;
fc->flags |= AFS_FS_CURSOR_STOP;
return false;
}
fc->cbi = afs_get_cb_interest(vnode->cb_interest);
read_lock(&cbi->server->fs_lock);
alist = rcu_dereference_protected(cbi->server->addresses,
lockdep_is_held(&cbi->server->fs_lock));
afs_get_addrlist(alist);
read_unlock(&cbi->server->fs_lock);
if (!alist) {
fc->ac.error = -ESTALE;
fc->flags |= AFS_FS_CURSOR_STOP;
return false;
}
fc->ac.alist = alist;
fc->ac.addr = NULL;
fc->ac.start = READ_ONCE(alist->index);
fc->ac.index = fc->ac.start;
fc->ac.error = 0;
fc->ac.begun = false;
goto iterate_address;
case 0:
default:
/* Success or local failure. Stop. */
fc->flags |= AFS_FS_CURSOR_STOP; fc->flags |= AFS_FS_CURSOR_STOP;
_leave(" = f [okay/local %d]", fc->ac.error);
return false; return false;
}
read_lock(&cbi->server->fs_lock); case -ECONNABORTED:
alist = afs_get_addrlist(cbi->server->addresses);
read_unlock(&cbi->server->fs_lock);
if (!alist) {
fc->ac.error = -ESTALE;
fc->flags |= AFS_FS_CURSOR_STOP; fc->flags |= AFS_FS_CURSOR_STOP;
_leave(" = f [abort]");
return false; return false;
case -ENETUNREACH:
case -EHOSTUNREACH:
case -ECONNREFUSED:
case -ETIMEDOUT:
case -ETIME:
_debug("no conn");
goto iterate_address;
} }
fc->ac.alist = alist; iterate_address:
fc->ac.error = 0; /* Iterate over the current server's address list to try and find an
return true; * address on which it will respond to us.
*/
if (afs_iterate_addresses(&fc->ac)) {
_leave(" = t");
return true;
}
afs_end_cursor(&fc->ac);
return false;
} }
/* /*
......
...@@ -284,8 +284,8 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -284,8 +284,8 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
* permitted to be accessed with this authorisation, and if so, what access it * permitted to be accessed with this authorisation, and if so, what access it
* is granted * is granted
*/ */
static int afs_check_permit(struct afs_vnode *vnode, struct key *key, int afs_check_permit(struct afs_vnode *vnode, struct key *key,
afs_access_t *_access) afs_access_t *_access)
{ {
struct afs_permits *permits; struct afs_permits *permits;
bool valid = false; bool valid = false;
......
...@@ -17,7 +17,7 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist) ...@@ -17,7 +17,7 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist)
{ {
int i; int i;
if (refcount_dec_and_test(&slist->usage)) { if (slist && refcount_dec_and_test(&slist->usage)) {
for (i = 0; i < slist->nr_servers; i++) { for (i = 0; i < slist->nr_servers; i++) {
afs_put_cb_interest(net, slist->servers[i].cb_interest); afs_put_cb_interest(net, slist->servers[i].cb_interest);
afs_put_server(net, slist->servers[i].server); afs_put_server(net, slist->servers[i].server);
......
...@@ -119,6 +119,11 @@ int afs_write_begin(struct file *file, struct address_space *mapping, ...@@ -119,6 +119,11 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
} }
if (f != t) { if (f != t) {
if (PageWriteback(page)) {
trace_afs_page_dirty(vnode, tracepoint_string("alrdy"),
page->index, priv);
goto flush_conflicting_write;
}
if (to < f || from > t) if (to < f || from > t)
goto flush_conflicting_write; goto flush_conflicting_write;
if (from < f) if (from < f)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment