Commit 0d744719 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull AFS callback promise fixes from David Howells:
 "This series fixes a bunch of problems in callback promise handling,
  where a callback promise indicates a promise on the part of the server
  to notify the client in the event of some sort of change to a file or
  volume. In the event of a break, the client has to go and refetch the
  client status from the server and discard any cached permission
  information as the ACL might have changed.

  The problem in the current code is that changes made by other clients
  aren't always noticed, primarily because the file status information
  and the callback information aren't updated in the same critical
  section, even if these are carried in the same reply from an RPC
  operation, and so the AFS_VNODE_CB_PROMISED flag is unreliable.

  Arranging for them to be done in the same critical section during
  reply decoding is tricky because of the FS.InlineBulkStatus op - which
  has all the statuses in the reply arriving and then all the callbacks,
  so they have to be buffered. It simplifies things a lot to move the
  critical section out of the decode phase and do it after the RPC
  function returns.

  Also new inodes (either newly fetched or newly created) aren't
  properly managed against a callback break happening before we get the
  local inode up and running.

  Fix this by:

   - There's now a combined file status and callback record (struct
     afs_status_cb) to carry both plus some flags.

   - Each operation wrapper function allocates sufficient afs_status_cb
     records for all the vnodes it is interested in and passes them into
     RPC operations to be filled in from the reply.

   - The FileStatus and CallBack record decoders no longer apply the
     new/revised status and callback information to the inode/vnode at
     the point of decoding and instead store the information into the
     record from (2).

   - afs_vnode_commit_status() then revises the file status, detects
     deletion and notes callback information inside of a single critical
     section. It also checks the callback break counters and cancels the
     callback promise if they changed during the operation.

     [*] Note that "callback break counters" are counters of server
     events that cancel one or more callback promises that the client
     thinks it has. The client counts the events and compares the
     counters before and after an operation to see if the callback
     promise it thinks it just got evaporated before it got recorded
     under lock.

   - Volume and server callback break counters are passed into
     afs_iget() allowing callback breaks concurrent with inode set up to
     be detected and the callback promise thence to be cancelled.

   - AFS validation checks are now done under RCU conditions using a
     read lock on cb_lock. This requires vnode->cb_interest to be made
     RCU safe.

   - If the checks in (6) fail, the callback breaker is then called
     under write lock on the cb_lock - but only if the callback break
     counter didn't change from the value read before the checks were
     made.

   - Results from FS.InlineBulkStatus that correspond to inodes we
     currently have in memory are now used to update those inodes'
     status and callback information rather than being discarded. This
     requires those inodes to be looked up before the RPC op is made and
     all their callback break values saved.

  To aid in this, the following changes have also been made:

   - Don't pass the vnode into the reply delivery functions or the
     decoders. The vnode shouldn't be altered anywhere in those paths.
     The only exception, for the moment, is for the call done hook for
     file lock ops that wants access to both the vnode and the call -
     this can be fixed at a later time.

   - Get rid of the call->reply[] void* array and replace it with named
     and typed members. This avoids confusion since different ops were
     mapping different reply[] members to different things.

   - Fix an order-1 kmalloc allocation in afs_do_lookup() and replace it
     with kvcalloc().

   - Always get the reply time. Since callback, lock and fileserver
     record expiry times are calculated for several RPCs, make this
     mandatory.

   - Call afs_pages_written_back() from the operation wrapper rather
     than from the delivery function.

   - Don't store the version and type from a callback promise in a reply
     as the information in them is of very limited use"

* tag 'afs-fixes-b-20190516' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Fix application of the results of a inline bulk status fetch
  afs: Pass pre-fetch server and volume break counts into afs_iget5_set()
  afs: Fix unlink to handle YFS.RemoveFile2 better
  afs: Clear AFS_VNODE_CB_PROMISED if we detect callback expiry
  afs: Make vnode->cb_interest RCU safe
  afs: Split afs_validate() so first part can be used under LOOKUP_RCU
  afs: Don't save callback version and type fields
  afs: Fix application of status and callback to be under same lock
  afs: Always get the reply time
  afs: Fix order-1 allocation in afs_do_lookup()
  afs: Get rid of afs_call::reply[]
  afs: Don't pass the vnode pointer through into the inline bulk status op
parents 227747fb 39db9815
......@@ -72,8 +72,8 @@ typedef enum {
struct afs_callback {
time64_t expires_at; /* Time at which expires */
unsigned version; /* Callback version */
afs_callback_type_t type; /* Type of callback */
//unsigned version; /* Callback version */
//afs_callback_type_t type; /* Type of callback */
};
struct afs_callback_break {
......@@ -147,6 +147,15 @@ struct afs_file_status {
u32 abort_code; /* Abort if bulk-fetching this failed */
};
struct afs_status_cb {
struct afs_file_status status;
struct afs_callback callback;
unsigned int cb_break; /* Pre-op callback break counter */
bool have_status; /* True if status record was retrieved */
bool have_cb; /* True if cb record was retrieved */
bool have_error; /* True if status.abort_code indicates an error */
};
/*
* AFS file status change request
*/
......
......@@ -94,15 +94,15 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
struct afs_server *server = entry->server;
again:
if (vnode->cb_interest &&
likely(vnode->cb_interest == entry->cb_interest))
vcbi = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->io_lock));
if (vcbi && likely(vcbi == entry->cb_interest))
return 0;
read_lock(&slist->lock);
cbi = afs_get_cb_interest(entry->cb_interest);
read_unlock(&slist->lock);
vcbi = vnode->cb_interest;
if (vcbi) {
if (vcbi == cbi) {
afs_put_cb_interest(afs_v2net(vnode), cbi);
......@@ -114,8 +114,9 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
*/
if (cbi && vcbi->server == cbi->server) {
write_seqlock(&vnode->cb_lock);
old = vnode->cb_interest;
vnode->cb_interest = cbi;
old = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->cb_lock.lock));
rcu_assign_pointer(vnode->cb_interest, cbi);
write_sequnlock(&vnode->cb_lock);
afs_put_cb_interest(afs_v2net(vnode), old);
return 0;
......@@ -160,8 +161,9 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
*/
write_seqlock(&vnode->cb_lock);
old = vnode->cb_interest;
vnode->cb_interest = cbi;
old = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->cb_lock.lock));
rcu_assign_pointer(vnode->cb_interest, cbi);
vnode->cb_s_break = cbi->server->cb_s_break;
vnode->cb_v_break = vnode->volume->cb_v_break;
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
......@@ -191,10 +193,11 @@ void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
vi = NULL;
write_unlock(&cbi->server->cb_break_lock);
kfree(vi);
if (vi)
kfree_rcu(vi, rcu);
afs_put_server(net, cbi->server);
}
kfree(cbi);
kfree_rcu(cbi, rcu);
}
}
......
......@@ -213,7 +213,7 @@ static int afs_find_cm_server_by_peer(struct afs_call *call)
return 0;
}
call->cm_server = server;
call->server = server;
return afs_record_cm_probe(call, server);
}
......@@ -234,7 +234,7 @@ static int afs_find_cm_server_by_uuid(struct afs_call *call,
return 0;
}
call->cm_server = server;
call->server = server;
return afs_record_cm_probe(call, server);
}
......@@ -260,8 +260,8 @@ static void SRXAFSCB_CallBack(struct work_struct *work)
* server holds up change visibility till it receives our reply so as
* to maintain cache coherency.
*/
if (call->cm_server)
afs_break_callbacks(call->cm_server, call->count, call->request);
if (call->server)
afs_break_callbacks(call->server, call->count, call->request);
afs_send_empty_reply(call);
afs_put_call(call);
......@@ -376,10 +376,10 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)
{
struct afs_call *call = container_of(work, struct afs_call, work);
_enter("{%p}", call->cm_server);
_enter("{%p}", call->server);
if (call->cm_server)
afs_init_callback_state(call->cm_server);
if (call->server)
afs_init_callback_state(call->server);
afs_send_empty_reply(call);
afs_put_call(call);
_leave("");
......
This diff is collapsed.
......@@ -24,21 +24,28 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
struct key *key)
{
struct afs_fs_cursor fc;
u64 dir_data_version = dvnode->status.data_version;
struct afs_status_cb *scb;
int ret = -ERESTARTSYS;
_enter("%pd,%pd", old, new);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
trace_afs_silly_rename(vnode, false);
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
afs_fs_rename(&fc, old->d_name.name,
dvnode, new->d_name.name,
dir_data_version, dir_data_version);
scb, scb);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&dir_data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
......@@ -64,6 +71,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
fsnotify_nameremove(old, 0);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
......@@ -143,31 +151,37 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode
struct dentry *dentry, struct key *key)
{
struct afs_fs_cursor fc;
u64 dir_data_version = dvnode->status.data_version;
struct afs_status_cb *scb;
int ret = -ERESTARTSYS;
_enter("");
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
trace_afs_silly_rename(vnode, true);
if (afs_begin_vnode_operation(&fc, dvnode, key, false)) {
afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
!test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
dir_data_version);
&scb[0], &scb[1]);
if (fc.ac.error != -ECONNABORTED ||
fc.ac.abort_code != RXGEN_OPCODE)
continue;
set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags);
}
afs_fs_remove(&fc, vnode, dentry->d_name.name, false,
dir_data_version);
afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&dir_data_version, &scb[0]);
ret = afs_end_vnode_operation(&fc);
if (ret == 0) {
drop_nlink(&vnode->vfs_inode);
......@@ -182,6 +196,7 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode
afs_edit_dir_for_unlink);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
......
......@@ -228,6 +228,7 @@ static void afs_file_readpage_read_complete(struct page *page,
int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *desc)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
int ret;
_enter("%s{%llx:%llu.%u},%x,,,",
......@@ -237,15 +238,22 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_fetch_data(&fc, desc);
afs_fs_fetch_data(&fc, scb, desc);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
......@@ -255,6 +263,7 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
&afs_v2net(vnode)->n_fetch_bytes);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
......@@ -405,10 +414,10 @@ static int afs_readpage(struct file *file, struct page *page)
/*
* Make pages available as they're filled.
*/
static void afs_readpages_page_done(struct afs_call *call, struct afs_read *req)
static void afs_readpages_page_done(struct afs_read *req)
{
#ifdef CONFIG_AFS_FSCACHE
struct afs_vnode *vnode = call->reply[0];
struct afs_vnode *vnode = req->vnode;
#endif
struct page *page = req->pages[req->index];
......@@ -462,6 +471,7 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping,
return -ENOMEM;
refcount_set(&req->usage, 1);
req->vnode = vnode;
req->page_done = afs_readpages_page_done;
req->pos = first->index;
req->pos <<= PAGE_SHIFT;
......
......@@ -74,7 +74,7 @@ static void afs_schedule_lock_extension(struct afs_vnode *vnode)
*/
void afs_lock_op_done(struct afs_call *call)
{
struct afs_vnode *vnode = call->reply[0];
struct afs_vnode *vnode = call->lvnode;
if (call->error == 0) {
spin_lock(&vnode->lock);
......@@ -182,6 +182,7 @@ static void afs_kill_lockers_enoent(struct afs_vnode *vnode)
static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
afs_lock_type_t type)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
......@@ -192,18 +193,23 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
vnode->fid.unique,
key_serial(key), type);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_set_lock(&fc, type);
afs_fs_set_lock(&fc, type, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
......@@ -213,6 +219,7 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
*/
static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
......@@ -223,18 +230,23 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
while (afs_select_current_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_extend_lock(&fc);
afs_fs_extend_lock(&fc, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
......@@ -244,6 +256,7 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
*/
static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
......@@ -254,18 +267,23 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
while (afs_select_current_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_release_lock(&fc);
afs_fs_release_lock(&fc, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
......@@ -733,7 +751,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
posix_test_lock(file, fl);
if (fl->fl_type == F_UNLCK) {
/* no local locks; consult the server */
ret = afs_fetch_status(vnode, key, false);
ret = afs_fetch_status(vnode, key, false, NULL);
if (ret < 0)
goto error;
......
......@@ -33,8 +33,8 @@ static bool afs_fs_probe_done(struct afs_server *server)
void afs_fileserver_probe_result(struct afs_call *call)
{
struct afs_addr_list *alist = call->alist;
struct afs_server *server = call->reply[0];
unsigned int server_index = (long)call->reply[1];
struct afs_server *server = call->server;
unsigned int server_index = call->server_index;
unsigned int index = call->addr_ix;
unsigned int rtt = UINT_MAX;
bool have_result = false;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -66,7 +66,8 @@ static bool afs_start_fs_iteration(struct afs_fs_cursor *fc,
fc->untried = (1UL << fc->server_list->nr_servers) - 1;
fc->index = READ_ONCE(fc->server_list->preferred);
cbi = vnode->cb_interest;
cbi = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->io_lock));
if (cbi) {
/* See if the vnode's preferred record is still available */
for (i = 0; i < fc->server_list->nr_servers; i++) {
......@@ -87,8 +88,8 @@ static bool afs_start_fs_iteration(struct afs_fs_cursor *fc,
/* Note that the callback promise is effectively broken */
write_seqlock(&vnode->cb_lock);
ASSERTCMP(cbi, ==, vnode->cb_interest);
vnode->cb_interest = NULL;
ASSERTCMP(cbi, ==, rcu_access_pointer(vnode->cb_interest));
rcu_assign_pointer(vnode->cb_interest, NULL);
if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
vnode->cb_break++;
write_sequnlock(&vnode->cb_lock);
......@@ -417,7 +418,9 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
if (error < 0)
goto failed_set_error;
fc->cbi = afs_get_cb_interest(vnode->cb_interest);
fc->cbi = afs_get_cb_interest(
rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->io_lock)));
read_lock(&server->fs_lock);
alist = rcu_dereference_protected(server->addresses,
......@@ -487,12 +490,15 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
{
struct afs_vnode *vnode = fc->vnode;
struct afs_cb_interest *cbi = vnode->cb_interest;
struct afs_cb_interest *cbi;
struct afs_addr_list *alist;
int error = fc->ac.error;
_enter("");
cbi = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->io_lock));
switch (error) {
case SHRT_MAX:
if (!cbi) {
......@@ -501,7 +507,7 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
return false;
}
fc->cbi = afs_get_cb_interest(vnode->cb_interest);
fc->cbi = afs_get_cb_interest(cbi);
read_lock(&cbi->server->fs_lock);
alist = rcu_dereference_protected(cbi->server->addresses,
......
......@@ -188,7 +188,7 @@ void afs_put_call(struct afs_call *call)
if (call->type->destructor)
call->type->destructor(call);
afs_put_server(call->net, call->cm_server);
afs_put_server(call->net, call->server);
afs_put_cb_interest(call->net, call->cbi);
afs_put_addrlist(call->alist);
kfree(call->request);
......@@ -534,11 +534,11 @@ static void afs_deliver_to_call(struct afs_call *call)
return;
}
if (call->want_reply_time &&
if (!call->have_reply_time &&
rxrpc_kernel_get_reply_time(call->net->socket,
call->rxcall,
&call->reply_time))
call->want_reply_time = false;
call->have_reply_time = true;
ret = call->type->deliver(call);
state = READ_ONCE(call->state);
......@@ -696,10 +696,9 @@ long afs_wait_for_call_to_complete(struct afs_call *call,
ret = ac->error;
switch (ret) {
case 0:
if (call->ret_reply0) {
ret = (long)call->reply[0];
call->reply[0] = NULL;
}
ret = call->ret0;
call->ret0 = 0;
/* Fall through */
case -ECONNABORTED:
ac->responded = true;
......
......@@ -116,10 +116,10 @@ static void afs_hash_permits(struct afs_permits *permits)
* as the ACL *may* have changed.
*/
void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
unsigned int cb_break)
unsigned int cb_break, struct afs_status_cb *scb)
{
struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL;
afs_access_t caller_access = READ_ONCE(vnode->status.caller_access);
afs_access_t caller_access = scb->status.caller_access;
size_t size = 0;
bool changed = false;
int i, j;
......@@ -146,7 +146,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
}
if (afs_cb_is_broken(cb_break, vnode,
vnode->cb_interest)) {
rcu_dereference(vnode->cb_interest))) {
changed = true;
break;
}
......@@ -176,7 +176,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
}
}
if (afs_cb_is_broken(cb_break, vnode, vnode->cb_interest))
if (afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)))
goto someone_else_changed_it;
/* We need a ref on any permits list we want to copy as we'll have to
......@@ -253,14 +253,16 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
kfree(new);
rcu_read_lock();
spin_lock(&vnode->lock);
zap = rcu_access_pointer(vnode->permit_cache);
if (!afs_cb_is_broken(cb_break, vnode, vnode->cb_interest) &&
if (!afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)) &&
zap == permits)
rcu_assign_pointer(vnode->permit_cache, replacement);
else
zap = replacement;
spin_unlock(&vnode->lock);
rcu_read_unlock();
afs_put_permits(zap);
out_put:
afs_put_permits(permits);
......@@ -320,13 +322,12 @@ int afs_check_permit(struct afs_vnode *vnode, struct key *key,
*/
_debug("no valid permit");
ret = afs_fetch_status(vnode, key, false);
ret = afs_fetch_status(vnode, key, false, _access);
if (ret < 0) {
*_access = 0;
_leave(" = %d", ret);
return ret;
}
*_access = vnode->status.caller_access;
}
_leave(" = 0 [access %x]", *_access);
......
......@@ -426,7 +426,7 @@ static int afs_set_super(struct super_block *sb, struct fs_context *fc)
static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
{
struct afs_super_info *as = AFS_FS_S(sb);
struct afs_fid fid;
struct afs_iget_data iget_data;
struct inode *inode = NULL;
int ret;
......@@ -451,11 +451,13 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
} else {
sprintf(sb->s_id, "%llu", as->volume->vid);
afs_activate_volume(as->volume);
fid.vid = as->volume->vid;
fid.vnode = 1;
fid.vnode_hi = 0;
fid.unique = 1;
inode = afs_iget(sb, ctx->key, &fid, NULL, NULL, NULL, NULL);
iget_data.fid.vid = as->volume->vid;
iget_data.fid.vnode = 1;
iget_data.fid.vnode_hi = 0;
iget_data.fid.unique = 1;
iget_data.cb_v_break = as->volume->cb_v_break;
iget_data.cb_s_break = 0;
inode = afs_iget(sb, ctx->key, &iget_data, NULL, NULL, NULL);
}
if (IS_ERR(inode))
......@@ -677,13 +679,12 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
vnode->volume = NULL;
vnode->lock_key = NULL;
vnode->permit_cache = NULL;
vnode->cb_interest = NULL;
RCU_INIT_POINTER(vnode->cb_interest, NULL);
#ifdef CONFIG_AFS_FSCACHE
vnode->cache = NULL;
#endif
vnode->flags = 1 << AFS_VNODE_UNSET;
vnode->cb_type = 0;
vnode->lock_state = AFS_VNODE_LOCK_NONE;
init_rwsem(&vnode->rmdir_lock);
......@@ -708,7 +709,7 @@ static void afs_destroy_inode(struct inode *inode)
_debug("DESTROY INODE %p", inode);
ASSERTCMP(vnode->cb_interest, ==, NULL);
ASSERTCMP(rcu_access_pointer(vnode->cb_interest), ==, NULL);
atomic_dec(&afs_count_active_inodes);
}
......@@ -749,7 +750,6 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc);
}
......
......@@ -33,8 +33,8 @@ static bool afs_vl_probe_done(struct afs_vlserver *server)
void afs_vlserver_probe_result(struct afs_call *call)
{
struct afs_addr_list *alist = call->alist;
struct afs_vlserver *server = call->reply[0];
unsigned int server_index = (long)call->reply[1];
struct afs_vlserver *server = call->vlserver;
unsigned int server_index = call->server_index;
unsigned int index = call->addr_ix;
unsigned int rtt = UINT_MAX;
bool have_result = false;
......
......@@ -34,7 +34,7 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
/* unmarshall the reply once we've received all of it */
uvldb = call->buffer;
entry = call->reply[0];
entry = call->ret_vldb;
nr_servers = ntohl(uvldb->nServers);
if (nr_servers > AFS_NMAXNSERVERS)
......@@ -110,7 +110,7 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
static void afs_destroy_vl_get_entry_by_name_u(struct afs_call *call)
{
kfree(call->reply[0]);
kfree(call->ret_vldb);
afs_flat_call_destructor(call);
}
......@@ -155,8 +155,7 @@ struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc,
}
call->key = vc->key;
call->reply[0] = entry;
call->ret_reply0 = true;
call->ret_vldb = entry;
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
/* Marshall the parameters */
......@@ -215,7 +214,7 @@ static int afs_deliver_vl_get_addrs_u(struct afs_call *call)
if (!alist)
return -ENOMEM;
alist->version = uniquifier;
call->reply[0] = alist;
call->ret_alist = alist;
call->count = count;
call->count2 = nentries;
call->unmarshall++;
......@@ -230,7 +229,7 @@ static int afs_deliver_vl_get_addrs_u(struct afs_call *call)
if (ret < 0)
return ret;
alist = call->reply[0];
alist = call->ret_alist;
bp = call->buffer;
count = min(call->count, 4U);
for (i = 0; i < count; i++)
......@@ -250,8 +249,7 @@ static int afs_deliver_vl_get_addrs_u(struct afs_call *call)
static void afs_vl_get_addrs_u_destructor(struct afs_call *call)
{
afs_put_server(call->net, (struct afs_server *)call->reply[0]);
kfree(call->reply[1]);
afs_put_addrlist(call->ret_alist);
return afs_flat_call_destructor(call);
}
......@@ -288,8 +286,7 @@ struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *vc,
return ERR_PTR(-ENOMEM);
call->key = vc->key;
call->reply[0] = NULL;
call->ret_reply0 = true;
call->ret_alist = NULL;
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
/* Marshall the parameters */
......@@ -360,9 +357,7 @@ static int afs_deliver_vl_get_capabilities(struct afs_call *call)
static void afs_destroy_vl_get_capabilities(struct afs_call *call)
{
struct afs_vlserver *server = call->reply[0];
afs_put_vlserver(call->net, server);
afs_put_vlserver(call->net, call->vlserver);
afs_flat_call_destructor(call);
}
......@@ -400,10 +395,9 @@ struct afs_call *afs_vl_get_capabilities(struct afs_net *net,
return ERR_PTR(-ENOMEM);
call->key = key;
call->reply[0] = afs_get_vlserver(server);
call->reply[1] = (void *)(long)server_index;
call->vlserver = afs_get_vlserver(server);
call->server_index = server_index;
call->upgrade = true;
call->want_reply_time = true;
call->async = true;
call->max_lifespan = AFS_PROBE_MAX_LIFESPAN;
......@@ -463,7 +457,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
if (!alist)
return -ENOMEM;
alist->version = uniquifier;
call->reply[0] = alist;
call->ret_alist = alist;
if (call->count == 0)
goto extract_volendpoints;
......@@ -491,7 +485,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
if (ret < 0)
return ret;
alist = call->reply[0];
alist = call->ret_alist;
bp = call->buffer;
switch (call->count2) {
case YFS_ENDPOINT_IPV4:
......@@ -612,7 +606,6 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
break;
}
alist = call->reply[0];
_leave(" = 0 [done]");
return 0;
}
......@@ -647,8 +640,7 @@ struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc,
return ERR_PTR(-ENOMEM);
call->key = vc->key;
call->reply[0] = NULL;
call->ret_reply0 = true;
call->ret_alist = NULL;
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
/* Marshall the parameters */
......
......@@ -313,6 +313,46 @@ static void afs_redirty_pages(struct writeback_control *wbc,
_leave("");
}
/*
* completion of write to server
*/
static void afs_pages_written_back(struct afs_vnode *vnode,
pgoff_t first, pgoff_t last)
{
struct pagevec pv;
unsigned long priv;
unsigned count, loop;
_enter("{%llx:%llu},{%lx-%lx}",
vnode->fid.vid, vnode->fid.vnode, first, last);
pagevec_init(&pv);
do {
_debug("done %lx-%lx", first, last);
count = last - first + 1;
if (count > PAGEVEC_SIZE)
count = PAGEVEC_SIZE;
pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping,
first, count, pv.pages);
ASSERTCMP(pv.nr, ==, count);
for (loop = 0; loop < count; loop++) {
priv = page_private(pv.pages[loop]);
trace_afs_page_dirty(vnode, tracepoint_string("clear"),
pv.pages[loop]->index, priv);
set_page_private(pv.pages[loop], 0);
end_page_writeback(pv.pages[loop]);
}
first += count;
__pagevec_release(&pv);
} while (first <= last);
afs_prune_wb_keys(vnode);
_leave("");
}
/*
* write to a file
*/
......@@ -322,6 +362,7 @@ static int afs_store_data(struct address_space *mapping,
{
struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_wb_key *wbk = NULL;
struct list_head *p;
int ret = -ENOKEY, ret2;
......@@ -333,6 +374,10 @@ static int afs_store_data(struct address_space *mapping,
vnode->fid.unique,
first, last, offset, to);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
return -ENOMEM;
spin_lock(&vnode->wb_lock);
p = vnode->wb_keys.next;
......@@ -351,6 +396,7 @@ static int afs_store_data(struct address_space *mapping,
spin_unlock(&vnode->wb_lock);
afs_put_wb_key(wbk);
kfree(scb);
_leave(" = %d [no keys]", ret);
return ret;
......@@ -362,13 +408,18 @@ static int afs_store_data(struct address_space *mapping,
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, wbk->key, false)) {
afs_dataversion_t data_version = vnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_store_data(&fc, mapping, first, last, offset, to);
afs_fs_store_data(&fc, mapping, first, last, offset, to, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
if (fc.ac.error == 0)
afs_pages_written_back(vnode, first, last);
ret = afs_end_vnode_operation(&fc);
}
......@@ -393,6 +444,7 @@ static int afs_store_data(struct address_space *mapping,
}
afs_put_wb_key(wbk);
kfree(scb);
_leave(" = %d", ret);
return ret;
}
......@@ -678,46 +730,6 @@ int afs_writepages(struct address_space *mapping,
return ret;
}
/*
* completion of write to server
*/
void afs_pages_written_back(struct afs_vnode *vnode, struct afs_call *call)
{
struct pagevec pv;
unsigned long priv;
unsigned count, loop;
pgoff_t first = call->first, last = call->last;
_enter("{%llx:%llu},{%lx-%lx}",
vnode->fid.vid, vnode->fid.vnode, first, last);
pagevec_init(&pv);
do {
_debug("done %lx-%lx", first, last);
count = last - first + 1;
if (count > PAGEVEC_SIZE)
count = PAGEVEC_SIZE;
pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping,
first, count, pv.pages);
ASSERTCMP(pv.nr, ==, count);
for (loop = 0; loop < count; loop++) {
priv = page_private(pv.pages[loop]);
trace_afs_page_dirty(vnode, tracepoint_string("clear"),
pv.pages[loop]->index, priv);
set_page_private(pv.pages[loop], 0);
end_page_writeback(pv.pages[loop]);
}
first += count;
__pagevec_release(&pv);
} while (first <= last);
afs_prune_wb_keys(vnode);
_leave("");
}
/*
* write to an AFS file
*/
......
......@@ -47,24 +47,34 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
void *buffer, size_t size)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
int ret = -ENOMEM;
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_scb;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
acl = afs_fs_fetch_acl(&fc);
acl = afs_fs_fetch_acl(&fc, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
......@@ -80,6 +90,9 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
}
key_put(key);
error_scb:
kfree(scb);
error:
return ret;
}
......@@ -92,22 +105,27 @@ static int afs_xattr_set_acl(const struct xattr_handler *handler,
const void *buffer, size_t size, int flags)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
int ret = -ENOMEM;
if (flags == XATTR_CREATE)
return -EINVAL;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl) {
key_put(key);
return -ENOMEM;
if (!acl)
goto error_scb;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_acl;
}
acl->size = size;
......@@ -115,18 +133,25 @@ static int afs_xattr_set_acl(const struct xattr_handler *handler,
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_store_acl(&fc, acl);
afs_fs_store_acl(&fc, acl, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(acl);
key_put(key);
error_acl:
kfree(acl);
error_scb:
kfree(scb);
error:
return ret;
}
......@@ -145,6 +170,7 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
void *buffer, size_t size)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct yfs_acl *yacl = NULL;
struct key *key;
......@@ -171,21 +197,28 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
else if (which == 3)
yacl->flags |= YFS_ACL_WANT_VOL_ACL;
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error_yacl;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_yacl;
goto error_scb;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
yfs_fs_fetch_opaque_acl(&fc, yacl);
yfs_fs_fetch_opaque_acl(&fc, yacl, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
......@@ -225,6 +258,8 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
error_key:
key_put(key);
error_scb:
kfree(scb);
error_yacl:
yfs_free_opaque_acl(yacl);
error:
......@@ -240,42 +275,54 @@ static int afs_xattr_set_yfs(const struct xattr_handler *handler,
const void *buffer, size_t size, int flags)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
int ret = -ENOMEM;
if (flags == XATTR_CREATE ||
strcmp(name, "acl") != 0)
return -EINVAL;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl) {
key_put(key);
return -ENOMEM;
}
if (!acl)
goto error_scb;
acl->size = size;
memcpy(acl->data, buffer, size);
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_acl;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
yfs_fs_store_opaque_acl2(&fc, acl);
yfs_fs_store_opaque_acl2(&fc, acl, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
error_acl:
kfree(acl);
key_put(key);
error_scb:
kfree(scb);
error:
return ret;
}
......
This diff is collapsed.
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