From 3f19b2ab97a97b413c24b66c67ae16daa4f56c35 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 1 Dec 2017 11:40:16 +0000 Subject: vfs, afs, ext4: Make the inode hash table RCU searchable Make the inode hash table RCU searchable so that searches that want to access or modify an inode without taking a ref on that inode can do so without taking the inode hash table lock. The main thing this requires is some RCU annotation on the list manipulation operations. Inodes are already freed by RCU in most cases. Users of this interface must take care as the inode may be still under construction or may be being torn down around them. There are at least three instances where this can be of use: (1) Testing whether the inode number iunique() is going to return is currently unique (the iunique_lock is still held). (2) Ext4 date stamp updating. (3) AFS callback breaking. Signed-off-by: David Howells Acked-by: Konstantin Khlebnikov cc: linux-ext4@vger.kernel.org cc: linux-afs@lists.infradead.org --- fs/afs/callback.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 2dca8df1a18d..0dcbd40732d1 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -252,6 +252,7 @@ static void afs_break_one_callback(struct afs_server *server, struct afs_vnode *vnode; struct inode *inode; + rcu_read_lock(); read_lock(&server->cb_break_lock); hlist_for_each_entry(vi, &server->cb_volumes, srv_link) { if (vi->vid < fid->vid) @@ -287,12 +288,16 @@ static void afs_break_one_callback(struct afs_server *server, } else { data.volume = NULL; data.fid = *fid; - inode = ilookup5_nowait(cbi->sb, fid->vnode, - afs_iget5_test, &data); + + /* See if we can find a matching inode - even an I_NEW + * inode needs to be marked as it can have its callback + * broken before we finish setting up the local inode. + */ + inode = find_inode_rcu(cbi->sb, fid->vnode, + afs_iget5_test, &data); if (inode) { vnode = AFS_FS_I(inode); afs_break_callback(vnode, afs_cb_break_for_callback); - iput(inode); } else { trace_afs_cb_miss(fid, afs_cb_break_for_callback); } @@ -301,6 +306,7 @@ static void afs_break_one_callback(struct afs_server *server, out: read_unlock(&server->cb_break_lock); + rcu_read_unlock(); } /* -- cgit v1.2.3-70-g09d2 From 13fcc6356a94558a0a4857dc00cd26b3834a1b3e Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Apr 2020 14:20:32 +0100 Subject: afs: Always include dir in bulk status fetch from afs_do_lookup() When a lookup is done in an AFS directory, the filesystem will speculate and fetch up to 49 other statuses for files in the same directory and fetch those as well, turning them into inodes or updating inodes that already exist. However, occasionally, a callback break might go missing due to NAT timing out, but the afs filesystem doesn't then realise that the directory is not up to date. Alleviate this by using one of the status slots to check the directory in which the lookup is being done. Reported-by: Dave Botsch Suggested-by: Jeffrey Altman Signed-off-by: David Howells --- fs/afs/dir.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/dir.c b/fs/afs/dir.c index d1e1caa23c8b..3c486340b220 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -658,7 +658,8 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, cookie->ctx.actor = afs_lookup_filldir; cookie->name = dentry->d_name; - cookie->nr_fids = 1; /* slot 0 is saved for the fid we actually want */ + cookie->nr_fids = 2; /* slot 0 is saved for the fid we actually want + * and slot 1 for the directory */ read_seqlock_excl(&dvnode->cb_lock); dcbi = rcu_dereference_protected(dvnode->cb_interest, @@ -709,7 +710,11 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, if (!cookie->inodes) goto out_s; - for (i = 1; i < cookie->nr_fids; i++) { + cookie->fids[1] = dvnode->fid; + cookie->statuses[1].cb_break = afs_calc_vnode_cb_break(dvnode); + cookie->inodes[1] = igrab(&dvnode->vfs_inode); + + for (i = 2; i < cookie->nr_fids; i++) { scb = &cookie->statuses[i]; /* Find any inodes that already exist and get their -- cgit v1.2.3-70-g09d2 From 810068059234551b6973b46ca572e654f0c5e665 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Apr 2020 17:05:28 +0100 Subject: afs: Use the serverUnique field in the UVLDB record to reduce rpc ops The U-version VLDB volume record retrieved by the VL.GetEntryByNameU rpc op carries a change counter (the serverUnique field) for each fileserver listed in the record as backing that volume. This is incremented whenever the registration details for a fileserver change (such as its address list). Note that the same value will be seen in all UVLDB records that refer to that fileserver. This should be checked before calling the VL server to re-query the address list for a fileserver. If it's the same, there's no point doing the query. Reported-by: Jeffrey Altman Signed-off-by: David Howells --- fs/afs/internal.h | 5 +++-- fs/afs/server.c | 26 ++++++++++++++------------ fs/afs/server_list.c | 3 ++- fs/afs/vlclient.c | 1 + 4 files changed, 20 insertions(+), 15 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 80255513e230..ee17c868ad2c 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -471,6 +471,7 @@ struct afs_vldb_entry { #define AFS_VLDB_QUERY_ERROR 4 /* - VL server returned error */ uuid_t fs_server[AFS_NMAXNSERVERS]; + u32 addr_version[AFS_NMAXNSERVERS]; /* Registration change counters */ u8 fs_mask[AFS_NMAXNSERVERS]; #define AFS_VOL_VTM_RW 0x01 /* R/W version of the volume is available (on this server) */ #define AFS_VOL_VTM_RO 0x02 /* R/O version of the volume is available (on this server) */ @@ -498,7 +499,6 @@ struct afs_server { struct hlist_node proc_link; /* Link in net->fs_proc */ struct afs_server *gc_next; /* Next server in manager's list */ time64_t put_time; /* Time at which last put */ - time64_t update_at; /* Time at which to next update the record */ unsigned long flags; #define AFS_SERVER_FL_NOT_READY 1 /* The record is not ready for use */ #define AFS_SERVER_FL_NOT_FOUND 2 /* VL server says no such server */ @@ -511,6 +511,7 @@ struct afs_server { #define AFS_SERVER_FL_IS_YFS 9 /* Server is YFS not AFS */ #define AFS_SERVER_FL_NO_RM2 10 /* Fileserver doesn't support YFS.RemoveFile2 */ #define AFS_SERVER_FL_HAVE_EPOCH 11 /* ->epoch is valid */ +#define AFS_SERVER_FL_NEEDS_UPDATE 12 /* Fileserver address list is out of date */ atomic_t usage; u32 addr_version; /* Address list version */ u32 cm_epoch; /* Server RxRPC epoch */ @@ -1241,7 +1242,7 @@ extern spinlock_t afs_server_peer_lock; extern struct afs_server *afs_find_server(struct afs_net *, const struct sockaddr_rxrpc *); extern struct afs_server *afs_find_server_by_uuid(struct afs_net *, const uuid_t *); -extern struct afs_server *afs_lookup_server(struct afs_cell *, struct key *, const uuid_t *); +extern struct afs_server *afs_lookup_server(struct afs_cell *, struct key *, const uuid_t *, u32); extern struct afs_server *afs_get_server(struct afs_server *, enum afs_server_trace); extern void afs_put_server(struct afs_net *, struct afs_server *, enum afs_server_trace); extern void afs_manage_servers(struct work_struct *); diff --git a/fs/afs/server.c b/fs/afs/server.c index 11b90ac7ea30..9e50ccde5d37 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -12,7 +12,6 @@ #include "protocol_yfs.h" static unsigned afs_server_gc_delay = 10; /* Server record timeout in seconds */ -static unsigned afs_server_update_delay = 30; /* Time till VLDB recheck in secs */ static atomic_t afs_server_debug_id; static void afs_inc_servers_outstanding(struct afs_net *net) @@ -218,7 +217,6 @@ static struct afs_server *afs_alloc_server(struct afs_net *net, RCU_INIT_POINTER(server->addresses, alist); server->addr_version = alist->version; server->uuid = *uuid; - server->update_at = ktime_get_real_seconds() + afs_server_update_delay; rwlock_init(&server->fs_lock); INIT_HLIST_HEAD(&server->cb_volumes); rwlock_init(&server->cb_break_lock); @@ -264,7 +262,7 @@ static struct afs_addr_list *afs_vl_lookup_addrs(struct afs_cell *cell, * Get or create a fileserver record. */ struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key, - const uuid_t *uuid) + const uuid_t *uuid, u32 addr_version) { struct afs_addr_list *alist; struct afs_server *server, *candidate; @@ -272,8 +270,11 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key, _enter("%p,%pU", cell->net, uuid); server = afs_find_server_by_uuid(cell->net, uuid); - if (server) + if (server) { + if (server->addr_version != addr_version) + set_bit(AFS_SERVER_FL_NEEDS_UPDATE, &server->flags); return server; + } alist = afs_vl_lookup_addrs(cell, key, uuid); if (IS_ERR(alist)) @@ -558,7 +559,6 @@ static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct a write_unlock(&server->fs_lock); } - server->update_at = ktime_get_real_seconds() + afs_server_update_delay; afs_put_addrlist(discard); _leave(" = t"); return true; @@ -569,8 +569,6 @@ static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct a */ bool afs_check_server_record(struct afs_fs_cursor *fc, struct afs_server *server) { - time64_t now = ktime_get_real_seconds(); - long diff; bool success; int ret, retries = 0; @@ -579,13 +577,16 @@ bool afs_check_server_record(struct afs_fs_cursor *fc, struct afs_server *server ASSERT(server); retry: - diff = READ_ONCE(server->update_at) - now; - if (diff > 0) { - _leave(" = t [not now %ld]", diff); - return true; - } + if (test_bit(AFS_SERVER_FL_UPDATING, &server->flags)) + goto wait; + if (test_bit(AFS_SERVER_FL_NEEDS_UPDATE, &server->flags)) + goto update; + _leave(" = t [good]"); + return true; +update: if (!test_and_set_bit_lock(AFS_SERVER_FL_UPDATING, &server->flags)) { + clear_bit(AFS_SERVER_FL_NEEDS_UPDATE, &server->flags); success = afs_update_server_record(fc, server); clear_bit_unlock(AFS_SERVER_FL_UPDATING, &server->flags); wake_up_bit(&server->flags, AFS_SERVER_FL_UPDATING); @@ -593,6 +594,7 @@ retry: return success; } +wait: ret = wait_on_bit(&server->flags, AFS_SERVER_FL_UPDATING, (fc->flags & AFS_FS_CURSOR_INTR) ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index 888d91d195d9..f567732df5cc 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -51,7 +51,8 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell, if (!(vldb->fs_mask[i] & type_mask)) continue; - server = afs_lookup_server(cell, key, &vldb->fs_server[i]); + server = afs_lookup_server(cell, key, &vldb->fs_server[i], + vldb->addr_version[i]); if (IS_ERR(server)) { ret = PTR_ERR(server); if (ret == -ENOENT || diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c index 516e9a3bb5b4..972dc5512f33 100644 --- a/fs/afs/vlclient.c +++ b/fs/afs/vlclient.c @@ -82,6 +82,7 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call) for (j = 0; j < 6; j++) uuid->node[j] = (u8)ntohl(xdr->node[j]); + entry->addr_version[n] = ntohl(uvldb->serverUnique[i]); entry->nr_servers++; } -- cgit v1.2.3-70-g09d2 From 977e5f8ed0ab2786755f8d2a96b78a3c7320f7c4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 17 Apr 2020 17:31:26 +0100 Subject: afs: Split the usage count on struct afs_server Split the usage count on the afs_server struct to have an active count that registers who's actually using it separately from the reference count on the object. This allows a future patch to dispatch polling probes without advancing the "unuse" time into the future each time we emit a probe, which would otherwise prevent unused server records from expiring. Included in this: (1) The latter part of afs_destroy_server() in which the RCU destruction of afs_server objects is invoked and the outstanding server count is decremented is split out into __afs_put_server(). (2) afs_put_server() now calls __afs_put_server() rather then setting the management timer. (3) The calls begun by afs_fs_give_up_all_callbacks() and afs_fs_get_capabilities() can now take a ref on the server record, so afs_destroy_server() can just drop its ref and needn't wait for the completion of these calls. They'll put the ref when they're done. (4) Because of (3), afs_fs_probe_done() no longer needs to wake up afs_destroy_server() with server->probe_outstanding. (5) afs_gc_servers can be simplified. It only needs to check if server->active is 0 rather than playing games with the refcount. (6) afs_manage_servers() can propose a server for gc if usage == 0 rather than if ref == 1. The gc is effected by (5). Signed-off-by: David Howells --- fs/afs/cmservice.c | 4 +- fs/afs/fs_probe.c | 1 - fs/afs/fsclient.c | 5 +- fs/afs/internal.h | 8 ++- fs/afs/proc.c | 9 +-- fs/afs/rxrpc.c | 2 +- fs/afs/server.c | 151 ++++++++++++++++++++++++++++++--------------- fs/afs/server_list.c | 4 +- include/trace/events/afs.h | 18 ++++-- 9 files changed, 131 insertions(+), 71 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 380ad5ace7cf..7dcbca3bf828 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -268,7 +268,9 @@ static void SRXAFSCB_CallBack(struct work_struct *work) * to maintain cache coherency. */ if (call->server) { - trace_afs_server(call->server, atomic_read(&call->server->usage), + trace_afs_server(call->server, + atomic_read(&call->server->ref), + atomic_read(&call->server->active), afs_server_trace_callback); afs_break_callbacks(call->server, call->count, call->request); } diff --git a/fs/afs/fs_probe.c b/fs/afs/fs_probe.c index 37d1bba57b00..d37d78eb84bd 100644 --- a/fs/afs/fs_probe.c +++ b/fs/afs/fs_probe.c @@ -16,7 +16,6 @@ static bool afs_fs_probe_done(struct afs_server *server) if (!atomic_dec_and_test(&server->probe_outstanding)) return false; - wake_up_var(&server->probe_outstanding); clear_bit_unlock(AFS_SERVER_FL_PROBING, &server->flags); wake_up_bit(&server->flags, AFS_SERVER_FL_PROBING); return true; diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index d2b3798c1932..3854d16e14b1 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -1842,7 +1842,7 @@ int afs_fs_give_up_all_callbacks(struct afs_net *net, bp = call->request; *bp++ = htonl(FSGIVEUPALLCALLBACKS); - /* Can't take a ref on server */ + call->server = afs_use_server(server, afs_server_trace_give_up_cb); afs_make_call(ac, call, GFP_NOFS); return afs_wait_for_call_to_complete(call, ac); } @@ -1924,7 +1924,7 @@ struct afs_call *afs_fs_get_capabilities(struct afs_net *net, return ERR_PTR(-ENOMEM); call->key = key; - call->server = afs_get_server(server, afs_server_trace_get_caps); + call->server = afs_use_server(server, afs_server_trace_get_caps); call->server_index = server_index; call->upgrade = true; call->async = true; @@ -1934,7 +1934,6 @@ struct afs_call *afs_fs_get_capabilities(struct afs_net *net, bp = call->request; *bp++ = htonl(FSGETCAPABILITIES); - /* Can't take a ref on server */ trace_afs_make_fs_call(call, NULL); afs_make_call(ac, call, GFP_NOFS); return call; diff --git a/fs/afs/internal.h b/fs/afs/internal.h index ee17c868ad2c..cb70e1c234cc 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -498,7 +498,7 @@ struct afs_server { struct hlist_node addr6_link; /* Link in net->fs_addresses6 */ struct hlist_node proc_link; /* Link in net->fs_proc */ struct afs_server *gc_next; /* Next server in manager's list */ - time64_t put_time; /* Time at which last put */ + time64_t unuse_time; /* Time at which last unused */ unsigned long flags; #define AFS_SERVER_FL_NOT_READY 1 /* The record is not ready for use */ #define AFS_SERVER_FL_NOT_FOUND 2 /* VL server says no such server */ @@ -512,7 +512,8 @@ struct afs_server { #define AFS_SERVER_FL_NO_RM2 10 /* Fileserver doesn't support YFS.RemoveFile2 */ #define AFS_SERVER_FL_HAVE_EPOCH 11 /* ->epoch is valid */ #define AFS_SERVER_FL_NEEDS_UPDATE 12 /* Fileserver address list is out of date */ - atomic_t usage; + atomic_t ref; /* Object refcount */ + atomic_t active; /* Active user count */ u32 addr_version; /* Address list version */ u32 cm_epoch; /* Server RxRPC epoch */ unsigned int debug_id; /* Debugging ID for traces */ @@ -1244,6 +1245,9 @@ extern struct afs_server *afs_find_server(struct afs_net *, extern struct afs_server *afs_find_server_by_uuid(struct afs_net *, const uuid_t *); extern struct afs_server *afs_lookup_server(struct afs_cell *, struct key *, const uuid_t *, u32); extern struct afs_server *afs_get_server(struct afs_server *, enum afs_server_trace); +extern struct afs_server *afs_use_server(struct afs_server *, enum afs_server_trace); +extern void afs_unuse_server(struct afs_net *, struct afs_server *, enum afs_server_trace); +extern void afs_unuse_server_notime(struct afs_net *, struct afs_server *, enum afs_server_trace); extern void afs_put_server(struct afs_net *, struct afs_server *, enum afs_server_trace); extern void afs_manage_servers(struct work_struct *); extern void afs_servers_timer(struct timer_list *); diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 468e1713bce1..9bce7898cd7d 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -378,19 +378,20 @@ static int afs_proc_servers_show(struct seq_file *m, void *v) int i; if (v == SEQ_START_TOKEN) { - seq_puts(m, "UUID USE ADDR\n"); + seq_puts(m, "UUID REF ACT ADDR\n"); return 0; } server = list_entry(v, struct afs_server, proc_link); alist = rcu_dereference(server->addresses); - seq_printf(m, "%pU %3d %pISpc%s\n", + seq_printf(m, "%pU %3d %3d %pISpc%s\n", &server->uuid, - atomic_read(&server->usage), + atomic_read(&server->ref), + atomic_read(&server->active), &alist->addrs[0].transport, alist->preferred == 0 ? "*" : ""); for (i = 1; i < alist->nr_addrs; i++) - seq_printf(m, " %pISpc%s\n", + seq_printf(m, " %pISpc%s\n", &alist->addrs[i].transport, alist->preferred == i ? "*" : ""); return 0; diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 1ecc67da6c1a..ab2962fff1fb 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -183,7 +183,7 @@ void afs_put_call(struct afs_call *call) if (call->type->destructor) call->type->destructor(call); - afs_put_server(call->net, call->server, afs_server_trace_put_call); + afs_unuse_server_notime(call->net, call->server, afs_server_trace_put_call); afs_put_cb_interest(call->net, call->cbi); afs_put_addrlist(call->alist); kfree(call->request); diff --git a/fs/afs/server.c b/fs/afs/server.c index 9e50ccde5d37..4969a681f8f5 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -25,6 +25,10 @@ static void afs_dec_servers_outstanding(struct afs_net *net) wake_up_var(&net->servers_outstanding); } +static struct afs_server *afs_maybe_use_server(struct afs_server *, + enum afs_server_trace); +static void __afs_put_server(struct afs_net *, struct afs_server *); + /* * Find a server by one of its addresses. */ @@ -40,7 +44,7 @@ struct afs_server *afs_find_server(struct afs_net *net, do { if (server) - afs_put_server(net, server, afs_server_trace_put_find_rsq); + afs_unuse_server_notime(net, server, afs_server_trace_put_find_rsq); server = NULL; read_seqbegin_or_lock(&net->fs_addr_lock, &seq); @@ -78,9 +82,9 @@ struct afs_server *afs_find_server(struct afs_net *net, } server = NULL; + continue; found: - if (server && !atomic_inc_not_zero(&server->usage)) - server = NULL; + server = afs_maybe_use_server(server, afs_server_trace_get_by_addr); } while (need_seqretry(&net->fs_addr_lock, seq)); @@ -91,7 +95,7 @@ struct afs_server *afs_find_server(struct afs_net *net, } /* - * Look up a server by its UUID + * Look up a server by its UUID and mark it active. */ struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uuid) { @@ -107,7 +111,7 @@ struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uu * changes. */ if (server) - afs_put_server(net, server, afs_server_trace_put_uuid_rsq); + afs_unuse_server(net, server, afs_server_trace_put_uuid_rsq); server = NULL; read_seqbegin_or_lock(&net->fs_lock, &seq); @@ -122,7 +126,7 @@ struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uu } else if (diff > 0) { p = p->rb_right; } else { - afs_get_server(server, afs_server_trace_get_by_uuid); + afs_use_server(server, afs_server_trace_get_by_uuid); break; } @@ -198,7 +202,7 @@ exists: } /* - * allocate a new server record + * Allocate a new server record and mark it active. */ static struct afs_server *afs_alloc_server(struct afs_net *net, const uuid_t *uuid, @@ -212,7 +216,8 @@ static struct afs_server *afs_alloc_server(struct afs_net *net, if (!server) goto enomem; - atomic_set(&server->usage, 1); + atomic_set(&server->ref, 1); + atomic_set(&server->active, 1); server->debug_id = atomic_inc_return(&afs_server_debug_id); RCU_INIT_POINTER(server->addresses, alist); server->addr_version = alist->version; @@ -224,7 +229,7 @@ static struct afs_server *afs_alloc_server(struct afs_net *net, spin_lock_init(&server->probe_lock); afs_inc_servers_outstanding(net); - trace_afs_server(server, 1, afs_server_trace_alloc); + trace_afs_server(server, 1, 1, afs_server_trace_alloc); _leave(" = %p", server); return server; @@ -292,7 +297,6 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key, kfree(candidate); } - _leave(" = %p{%d}", server, atomic_read(&server->usage)); return server; } @@ -328,9 +332,38 @@ void afs_servers_timer(struct timer_list *timer) struct afs_server *afs_get_server(struct afs_server *server, enum afs_server_trace reason) { - unsigned int u = atomic_inc_return(&server->usage); + unsigned int u = atomic_inc_return(&server->ref); + + trace_afs_server(server, u, atomic_read(&server->active), reason); + return server; +} + +/* + * Try to get a reference on a server object. + */ +static struct afs_server *afs_maybe_use_server(struct afs_server *server, + enum afs_server_trace reason) +{ + unsigned int r = atomic_fetch_add_unless(&server->ref, 1, 0); + unsigned int a; + + if (r == 0) + return NULL; + + a = atomic_inc_return(&server->active); + trace_afs_server(server, r, a, reason); + return server; +} + +/* + * Get an active count on a server object. + */ +struct afs_server *afs_use_server(struct afs_server *server, enum afs_server_trace reason) +{ + unsigned int r = atomic_inc_return(&server->ref); + unsigned int a = atomic_inc_return(&server->active); - trace_afs_server(server, u, reason); + trace_afs_server(server, r, a, reason); return server; } @@ -345,28 +378,56 @@ void afs_put_server(struct afs_net *net, struct afs_server *server, if (!server) return; - server->put_time = ktime_get_real_seconds(); - - usage = atomic_dec_return(&server->usage); + usage = atomic_dec_return(&server->ref); + trace_afs_server(server, usage, atomic_read(&server->active), reason); + if (unlikely(usage == 0)) + __afs_put_server(net, server); +} - trace_afs_server(server, usage, reason); +/* + * Drop an active count on a server object without updating the last-unused + * time. + */ +void afs_unuse_server_notime(struct afs_net *net, struct afs_server *server, + enum afs_server_trace reason) +{ + if (server) { + unsigned int active = atomic_dec_return(&server->active); - if (likely(usage > 0)) - return; + if (active == 0) + afs_set_server_timer(net, afs_server_gc_delay); + afs_put_server(net, server, reason); + } +} - afs_set_server_timer(net, afs_server_gc_delay); +/* + * Drop an active count on a server object. + */ +void afs_unuse_server(struct afs_net *net, struct afs_server *server, + enum afs_server_trace reason) +{ + if (server) { + server->unuse_time = ktime_get_real_seconds(); + afs_unuse_server_notime(net, server, reason); + } } static void afs_server_rcu(struct rcu_head *rcu) { struct afs_server *server = container_of(rcu, struct afs_server, rcu); - trace_afs_server(server, atomic_read(&server->usage), - afs_server_trace_free); + trace_afs_server(server, atomic_read(&server->ref), + atomic_read(&server->active), afs_server_trace_free); afs_put_addrlist(rcu_access_pointer(server->addresses)); kfree(server); } +static void __afs_put_server(struct afs_net *net, struct afs_server *server) +{ + call_rcu(&server->rcu, afs_server_rcu); + afs_dec_servers_outstanding(net); +} + /* * destroy a dead server */ @@ -379,19 +440,10 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server) .error = 0, }; - trace_afs_server(server, atomic_read(&server->usage), - afs_server_trace_give_up_cb); - if (test_bit(AFS_SERVER_FL_MAY_HAVE_CB, &server->flags)) afs_fs_give_up_all_callbacks(net, server, &ac, NULL); - wait_var_event(&server->probe_outstanding, - atomic_read(&server->probe_outstanding) == 0); - - trace_afs_server(server, atomic_read(&server->usage), - afs_server_trace_destroy); - call_rcu(&server->rcu, afs_server_rcu); - afs_dec_servers_outstanding(net); + afs_put_server(net, server, afs_server_trace_destroy); } /* @@ -400,31 +452,28 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server) static void afs_gc_servers(struct afs_net *net, struct afs_server *gc_list) { struct afs_server *server; - bool deleted; - int usage; + int active; while ((server = gc_list)) { gc_list = server->gc_next; write_seqlock(&net->fs_lock); - usage = 1; - deleted = atomic_try_cmpxchg(&server->usage, &usage, 0); - trace_afs_server(server, usage, afs_server_trace_gc); - if (deleted) { + + active = atomic_read(&server->active); + if (active == 0) { + trace_afs_server(server, atomic_read(&server->ref), + active, afs_server_trace_gc); rb_erase(&server->uuid_rb, &net->fs_servers); hlist_del_rcu(&server->proc_link); - } - write_sequnlock(&net->fs_lock); - - if (deleted) { - write_seqlock(&net->fs_addr_lock); if (!hlist_unhashed(&server->addr4_link)) hlist_del_rcu(&server->addr4_link); if (!hlist_unhashed(&server->addr6_link)) hlist_del_rcu(&server->addr6_link); - write_sequnlock(&net->fs_addr_lock); - afs_destroy_server(net, server); } + write_sequnlock(&net->fs_lock); + + if (active == 0) + afs_destroy_server(net, server); } } @@ -453,15 +502,14 @@ void afs_manage_servers(struct work_struct *work) for (cursor = rb_first(&net->fs_servers); cursor; cursor = rb_next(cursor)) { struct afs_server *server = rb_entry(cursor, struct afs_server, uuid_rb); - int usage = atomic_read(&server->usage); + int active = atomic_read(&server->active); - _debug("manage %pU %u", &server->uuid, usage); + _debug("manage %pU %u", &server->uuid, active); - ASSERTCMP(usage, >=, 1); - ASSERTIFCMP(purging, usage, ==, 1); + ASSERTIFCMP(purging, active, ==, 0); - if (usage == 1) { - time64_t expire_at = server->put_time; + if (active == 0) { + time64_t expire_at = server->unuse_time; if (!test_bit(AFS_SERVER_FL_VL_FAIL, &server->flags) && !test_bit(AFS_SERVER_FL_NOT_FOUND, &server->flags)) @@ -532,7 +580,8 @@ static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct a _enter(""); - trace_afs_server(server, atomic_read(&server->usage), afs_server_trace_update); + trace_afs_server(server, atomic_read(&server->ref), atomic_read(&server->active), + afs_server_trace_update); alist = afs_vl_lookup_addrs(fc->vnode->volume->cell, fc->key, &server->uuid); diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index f567732df5cc..b77e50f62459 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -16,8 +16,8 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist) if (slist && refcount_dec_and_test(&slist->usage)) { for (i = 0; i < slist->nr_servers; i++) { afs_put_cb_interest(net, slist->servers[i].cb_interest); - afs_put_server(net, slist->servers[i].server, - afs_server_trace_put_slist); + afs_unuse_server(net, slist->servers[i].server, + afs_server_trace_put_slist); } kfree(slist); } diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index c612cabbc378..f9691f69b2d6 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -33,6 +33,7 @@ enum afs_server_trace { afs_server_trace_destroy, afs_server_trace_free, afs_server_trace_gc, + afs_server_trace_get_by_addr, afs_server_trace_get_by_uuid, afs_server_trace_get_caps, afs_server_trace_get_install, @@ -241,6 +242,7 @@ enum afs_cb_break_reason { EM(afs_server_trace_destroy, "DESTROY ") \ EM(afs_server_trace_free, "FREE ") \ EM(afs_server_trace_gc, "GC ") \ + EM(afs_server_trace_get_by_addr, "GET addr ") \ EM(afs_server_trace_get_by_uuid, "GET uuid ") \ EM(afs_server_trace_get_caps, "GET caps ") \ EM(afs_server_trace_get_install, "GET inst ") \ @@ -1271,26 +1273,30 @@ TRACE_EVENT(afs_cb_miss, ); TRACE_EVENT(afs_server, - TP_PROTO(struct afs_server *server, int usage, enum afs_server_trace reason), + TP_PROTO(struct afs_server *server, int ref, int active, + enum afs_server_trace reason), - TP_ARGS(server, usage, reason), + TP_ARGS(server, ref, active, reason), TP_STRUCT__entry( __field(unsigned int, server ) - __field(int, usage ) + __field(int, ref ) + __field(int, active ) __field(int, reason ) ), TP_fast_assign( __entry->server = server->debug_id; - __entry->usage = usage; + __entry->ref = ref; + __entry->active = active; __entry->reason = reason; ), - TP_printk("s=%08x %s u=%d", + TP_printk("s=%08x %s u=%d a=%d", __entry->server, __print_symbolic(__entry->reason, afs_server_traces), - __entry->usage) + __entry->ref, + __entry->active) ); #endif /* _TRACE_AFS_H */ -- cgit v1.2.3-70-g09d2 From f6cbb368bcb0bc4fa7c11554d5293658bb4b26a2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 24 Apr 2020 15:10:00 +0100 Subject: afs: Actively poll fileservers to maintain NAT or firewall openings When an AFS client accesses a file, it receives a limited-duration callback promise that the server will notify it if another client changes a file. This callback duration can be a few hours in length. If a client mounts a volume and then an application prevents it from being unmounted, say by chdir'ing into it, but then does nothing for some time, the rxrpc_peer record will expire and rxrpc-level keepalive will cease. If there is NAT or a firewall between the client and the server, the route back for the server may close after a comparatively short duration, meaning that attempts by the server to notify the client may then bounce. The client, however, may (so far as it knows) still have a valid unexpired promise and will then rely on its cached data and will not see changes made on the server by a third party until it incidentally rechecks the status or the promise needs renewal. To deal with this, the client needs to regularly probe the server. This has two effects: firstly, it keeps a route open back for the server, and secondly, it causes the server to disgorge any notifications that got queued up because they couldn't be sent. Fix this by adding a mechanism to emit regular probes. Two levels of probing are made available: Under normal circumstances the 'slow' queue will be used for a fileserver - this just probes the preferred address once every 5 mins or so; however, if server fails to respond to any probes, the server will shift to the 'fast' queue from which all its interfaces will be probed every 30s. When it finally responds, the record will switch back to the slow queue. Further notes: (1) Probing is now no longer driven from the fileserver rotation algorithm. (2) Probes are dispatched to all interfaces on a fileserver when that an afs_server object is set up to record it. (3) The afs_server object is removed from the probe queues when we start to probe it. afs_is_probing_server() returns true if it's not listed - ie. it's undergoing probing. (4) The afs_server object is added back on to the probe queue when the final outstanding probe completes, but the probed_at time is set when we're about to launch a probe so that it's not dependent on the probe duration. (5) The timer and the work item added for this must be handed a count on net->servers_outstanding, which they hand on or release. This makes sure that network namespace cleanup waits for them. Fixes: d2ddc776a458 ("afs: Overhaul volume and server record caching and fileserver rotation") Reported-by: Dave Botsch Signed-off-by: David Howells --- fs/afs/cmservice.c | 2 +- fs/afs/fs_probe.c | 277 ++++++++++++++++++++++++++++++++++----------- fs/afs/fsclient.c | 21 ++-- fs/afs/internal.h | 41 +++++-- fs/afs/main.c | 5 +- fs/afs/rotate.c | 7 +- fs/afs/server.c | 19 ++-- fs/afs/volume.c | 24 ++-- include/trace/events/afs.h | 4 + 9 files changed, 281 insertions(+), 119 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 7dcbca3bf828..7ae88958051f 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -157,7 +157,7 @@ static int afs_record_cm_probe(struct afs_call *call, struct afs_server *server) _enter(""); if (test_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags) && - !test_bit(AFS_SERVER_FL_PROBING, &server->flags)) { + !afs_is_probing_server(server)) { if (server->cm_epoch == call->epoch) return 0; diff --git a/fs/afs/fs_probe.c b/fs/afs/fs_probe.c index d37d78eb84bd..442b5e7944ff 100644 --- a/fs/afs/fs_probe.c +++ b/fs/afs/fs_probe.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* AFS fileserver probing * - * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2018, 2020 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ @@ -11,14 +11,83 @@ #include "internal.h" #include "protocol_yfs.h" -static bool afs_fs_probe_done(struct afs_server *server) +static unsigned int afs_fs_probe_fast_poll_interval = 30 * HZ; +static unsigned int afs_fs_probe_slow_poll_interval = 5 * 60 * HZ; + +/* + * Start the probe polling timer. We have to supply it with an inc on the + * outstanding server count. + */ +static void afs_schedule_fs_probe(struct afs_net *net, + struct afs_server *server, bool fast) +{ + unsigned long atj; + + if (!net->live) + return; + + atj = server->probed_at; + atj += fast ? afs_fs_probe_fast_poll_interval : afs_fs_probe_slow_poll_interval; + + afs_inc_servers_outstanding(net); + if (timer_reduce(&net->fs_probe_timer, atj)) + afs_dec_servers_outstanding(net); +} + +/* + * Handle the completion of a set of probes. + */ +static void afs_finished_fs_probe(struct afs_net *net, struct afs_server *server) +{ + bool responded = server->probe.responded; + + write_seqlock(&net->fs_lock); + if (responded) + list_add_tail(&server->probe_link, &net->fs_probe_slow); + else + list_add_tail(&server->probe_link, &net->fs_probe_fast); + write_sequnlock(&net->fs_lock); + + afs_schedule_fs_probe(net, server, !responded); +} + +/* + * Handle the completion of a probe. + */ +static void afs_done_one_fs_probe(struct afs_net *net, struct afs_server *server) +{ + _enter(""); + + if (atomic_dec_and_test(&server->probe_outstanding)) + afs_finished_fs_probe(net, server); + + wake_up_all(&server->probe_wq); +} + +/* + * Handle inability to send a probe due to ENOMEM when trying to allocate a + * call struct. + */ +static void afs_fs_probe_not_done(struct afs_net *net, + struct afs_server *server, + struct afs_addr_cursor *ac) { - if (!atomic_dec_and_test(&server->probe_outstanding)) - return false; + struct afs_addr_list *alist = ac->alist; + unsigned int index = ac->index; + + _enter(""); - clear_bit_unlock(AFS_SERVER_FL_PROBING, &server->flags); - wake_up_bit(&server->flags, AFS_SERVER_FL_PROBING); - return true; + trace_afs_io_error(0, -ENOMEM, afs_io_error_fs_probe_fail); + spin_lock(&server->probe_lock); + + server->probe.local_failure = true; + if (server->probe.error == 0) + server->probe.error = -ENOMEM; + + set_bit(index, &alist->failed); + + spin_unlock(&server->probe_lock); + return afs_done_one_fs_probe(net, server); } /* @@ -29,10 +98,8 @@ void afs_fileserver_probe_result(struct afs_call *call) { struct afs_addr_list *alist = call->alist; struct afs_server *server = call->server; - unsigned int server_index = call->server_index; unsigned int index = call->addr_ix; unsigned int rtt_us = 0; - bool have_result = false; int ret = call->error; _enter("%pU,%u", &server->uuid, index); @@ -51,8 +118,9 @@ void afs_fileserver_probe_result(struct afs_call *call) goto responded; case -ENOMEM: case -ENONET: + clear_bit(index, &alist->responded); server->probe.local_failure = true; - afs_io_error(call, afs_io_error_fs_probe_fail); + trace_afs_io_error(call->debug_id, ret, afs_io_error_fs_probe_fail); goto out; case -ECONNRESET: /* Responded, but call expired. */ case -ERFKILL: @@ -71,12 +139,11 @@ void afs_fileserver_probe_result(struct afs_call *call) server->probe.error == -ETIMEDOUT || server->probe.error == -ETIME)) server->probe.error = ret; - afs_io_error(call, afs_io_error_fs_probe_fail); + trace_afs_io_error(call->debug_id, ret, afs_io_error_fs_probe_fail); goto out; } responded: - set_bit(index, &alist->responded); clear_bit(index, &alist->failed); if (call->service_id == YFS_FS_SERVICE) { @@ -95,38 +162,31 @@ responded: if (rtt_us < server->probe.rtt) { server->probe.rtt = rtt_us; alist->preferred = index; - have_result = true; } smp_wmb(); /* Set rtt before responded. */ server->probe.responded = true; - set_bit(AFS_SERVER_FL_PROBED, &server->flags); + set_bit(index, &alist->responded); out: spin_unlock(&server->probe_lock); - _debug("probe [%u][%u] %pISpc rtt=%u ret=%d", - server_index, index, &alist->addrs[index].transport, rtt_us, ret); + _debug("probe %pU [%u] %pISpc rtt=%u ret=%d", + &server->uuid, index, &alist->addrs[index].transport, + rtt_us, ret); - have_result |= afs_fs_probe_done(server); - if (have_result) - wake_up_all(&server->probe_wq); + return afs_done_one_fs_probe(call->net, server); } /* - * Probe all of a fileserver's addresses to find out the best route and to - * query its capabilities. + * Probe one or all of a fileserver's addresses to find out the best route and + * to query its capabilities. */ -static int afs_do_probe_fileserver(struct afs_net *net, - struct afs_server *server, - struct key *key, - unsigned int server_index, - struct afs_error *_e) +void afs_fs_probe_fileserver(struct afs_net *net, struct afs_server *server, + struct key *key, bool all) { struct afs_addr_cursor ac = { .index = 0, }; - struct afs_call *call; - bool in_progress = false; _enter("%pU", &server->uuid); @@ -136,50 +196,25 @@ static int afs_do_probe_fileserver(struct afs_net *net, afs_get_addrlist(ac.alist); read_unlock(&server->fs_lock); - atomic_set(&server->probe_outstanding, ac.alist->nr_addrs); + server->probed_at = jiffies; + atomic_set(&server->probe_outstanding, all ? ac.alist->nr_addrs : 1); memset(&server->probe, 0, sizeof(server->probe)); server->probe.rtt = UINT_MAX; - for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) { - call = afs_fs_get_capabilities(net, server, &ac, key, server_index); - if (!IS_ERR(call)) { - afs_put_call(call); - in_progress = true; - } else { - afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code); - } - } - - if (!in_progress) - afs_fs_probe_done(server); - afs_put_addrlist(ac.alist); - return in_progress; -} + ac.index = ac.alist->preferred; + if (ac.index < 0 || ac.index >= ac.alist->nr_addrs) + all = true; -/* - * Send off probes to all unprobed servers. - */ -int afs_probe_fileservers(struct afs_net *net, struct key *key, - struct afs_server_list *list) -{ - struct afs_server *server; - struct afs_error e; - bool in_progress = false; - int i; - - e.error = 0; - e.responded = false; - for (i = 0; i < list->nr_servers; i++) { - server = list->servers[i].server; - if (test_bit(AFS_SERVER_FL_PROBED, &server->flags)) - continue; - - if (!test_and_set_bit_lock(AFS_SERVER_FL_PROBING, &server->flags) && - afs_do_probe_fileserver(net, server, key, i, &e)) - in_progress = true; + if (all) { + for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) + if (!afs_fs_get_capabilities(net, server, &ac, key)) + afs_fs_probe_not_done(net, server, &ac); + } else { + if (!afs_fs_get_capabilities(net, server, &ac, key)) + afs_fs_probe_not_done(net, server, &ac); } - return in_progress ? 0 : e.error; + afs_put_addrlist(ac.alist); } /* @@ -199,7 +234,7 @@ int afs_wait_for_fs_probes(struct afs_server_list *slist, unsigned long untried) for (i = 0; i < slist->nr_servers; i++) { if (test_bit(i, &untried)) { server = slist->servers[i].server; - if (!test_bit(AFS_SERVER_FL_PROBING, &server->flags)) + if (!atomic_read(&server->probe_outstanding)) __clear_bit(i, &untried); if (server->probe.responded) have_responders = true; @@ -229,7 +264,7 @@ int afs_wait_for_fs_probes(struct afs_server_list *slist, unsigned long untried) server = slist->servers[i].server; if (server->probe.responded) goto stop; - if (test_bit(AFS_SERVER_FL_PROBING, &server->flags)) + if (atomic_read(&server->probe_outstanding)) still_probing = true; } } @@ -264,3 +299,109 @@ stop: slist->preferred = pref; return 0; } + +/* + * Probe timer. We have an increment on fs_outstanding that we need to pass + * along to the work item. + */ +void afs_fs_probe_timer(struct timer_list *timer) +{ + struct afs_net *net = container_of(timer, struct afs_net, fs_probe_timer); + + if (!queue_work(afs_wq, &net->fs_prober)) + afs_dec_servers_outstanding(net); +} + +/* + * Dispatch a probe to a server. + */ +static void afs_dispatch_fs_probe(struct afs_net *net, struct afs_server *server, bool all) + __releases(&net->fs_lock) +{ + struct key *key = NULL; + + /* We remove it from the queues here - it will be added back to + * one of the queues on the completion of the probe. + */ + list_del_init(&server->probe_link); + + afs_get_server(server, afs_server_trace_get_probe); + write_sequnlock(&net->fs_lock); + + afs_fs_probe_fileserver(net, server, key, all); + afs_put_server(net, server, afs_server_trace_put_probe); +} + +/* + * Probe dispatcher to regularly dispatch probes to keep NAT alive. + */ +void afs_fs_probe_dispatcher(struct work_struct *work) +{ + struct afs_net *net = container_of(work, struct afs_net, fs_prober); + struct afs_server *fast, *slow, *server; + unsigned long nowj, timer_at, poll_at; + bool first_pass = true, set_timer = false; + + if (!net->live) + return; + + _enter(""); + + if (list_empty(&net->fs_probe_fast) && list_empty(&net->fs_probe_slow)) { + _leave(" [none]"); + return; + } + +again: + write_seqlock(&net->fs_lock); + + fast = slow = server = NULL; + nowj = jiffies; + timer_at = nowj + MAX_JIFFY_OFFSET; + + if (!list_empty(&net->fs_probe_fast)) { + fast = list_first_entry(&net->fs_probe_fast, struct afs_server, probe_link); + poll_at = fast->probed_at + afs_fs_probe_fast_poll_interval; + if (time_before(nowj, poll_at)) { + timer_at = poll_at; + set_timer = true; + fast = NULL; + } + } + + if (!list_empty(&net->fs_probe_slow)) { + slow = list_first_entry(&net->fs_probe_slow, struct afs_server, probe_link); + poll_at = slow->probed_at + afs_fs_probe_slow_poll_interval; + if (time_before(nowj, poll_at)) { + if (time_before(poll_at, timer_at)) + timer_at = poll_at; + set_timer = true; + slow = NULL; + } + } + + server = fast ?: slow; + if (server) + _debug("probe %pU", &server->uuid); + + if (server && (first_pass || !need_resched())) { + afs_dispatch_fs_probe(net, server, server == fast); + first_pass = false; + goto again; + } + + write_sequnlock(&net->fs_lock); + + if (server) { + if (!queue_work(afs_wq, &net->fs_prober)) + afs_dec_servers_outstanding(net); + _leave(" [requeue]"); + } else if (set_timer) { + if (timer_reduce(&net->fs_probe_timer, timer_at)) + afs_dec_servers_outstanding(net); + _leave(" [timer]"); + } else { + afs_dec_servers_outstanding(net); + _leave(" [quiesce]"); + } +} diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 3854d16e14b1..401de063996c 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -1905,14 +1905,13 @@ static const struct afs_call_type afs_RXFSGetCapabilities = { }; /* - * Probe a fileserver for the capabilities that it supports. This can - * return up to 196 words. - */ -struct afs_call *afs_fs_get_capabilities(struct afs_net *net, - struct afs_server *server, - struct afs_addr_cursor *ac, - struct key *key, - unsigned int server_index) + * Probe a fileserver for the capabilities that it supports. This RPC can + * reply with up to 196 words. The operation is asynchronous and if we managed + * to allocate a call, true is returned the result is delivered through the + * ->done() - otherwise we return false to indicate we didn't even try. + */ +bool afs_fs_get_capabilities(struct afs_net *net, struct afs_server *server, + struct afs_addr_cursor *ac, struct key *key) { struct afs_call *call; __be32 *bp; @@ -1921,11 +1920,10 @@ struct afs_call *afs_fs_get_capabilities(struct afs_net *net, call = afs_alloc_flat_call(net, &afs_RXFSGetCapabilities, 1 * 4, 16 * 4); if (!call) - return ERR_PTR(-ENOMEM); + return false; call->key = key; call->server = afs_use_server(server, afs_server_trace_get_caps); - call->server_index = server_index; call->upgrade = true; call->async = true; call->max_lifespan = AFS_PROBE_MAX_LIFESPAN; @@ -1936,7 +1934,8 @@ struct afs_call *afs_fs_get_capabilities(struct afs_net *net, trace_afs_make_fs_call(call, NULL); afs_make_call(ac, call, GFP_NOFS); - return call; + afs_put_call(call); + return true; } /* diff --git a/fs/afs/internal.h b/fs/afs/internal.h index cb70e1c234cc..61320a632e15 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -90,7 +90,6 @@ struct afs_addr_list { unsigned char nr_ipv4; /* Number of IPv4 addresses */ enum dns_record_source source:8; enum dns_lookup_status status:8; - unsigned long probed; /* Mask of servers that have been probed */ unsigned long failed; /* Mask of addrs that failed locally/ICMP */ unsigned long responded; /* Mask of addrs that responded */ struct sockaddr_rxrpc addrs[]; @@ -299,9 +298,10 @@ struct afs_net { * cell, but in practice, people create aliases and subsets and there's * no easy way to distinguish them. */ - seqlock_t fs_lock; /* For fs_servers */ + seqlock_t fs_lock; /* For fs_servers, fs_probe_*, fs_proc */ struct rb_root fs_servers; /* afs_server (by server UUID or address) */ - struct list_head fs_updates; /* afs_server (by update_at) */ + struct list_head fs_probe_fast; /* List of afs_server to probe at 30s intervals */ + struct list_head fs_probe_slow; /* List of afs_server to probe at 5m intervals */ struct hlist_head fs_proc; /* procfs servers list */ struct hlist_head fs_addresses4; /* afs_server (by lowest IPv4 addr) */ @@ -310,6 +310,9 @@ struct afs_net { struct work_struct fs_manager; struct timer_list fs_timer; + + struct work_struct fs_prober; + struct timer_list fs_probe_timer; atomic_t servers_outstanding; /* File locking renewal management */ @@ -493,7 +496,8 @@ struct afs_server { }; struct afs_addr_list __rcu *addresses; - struct rb_node uuid_rb; /* Link in net->servers */ + struct rb_node uuid_rb; /* Link in net->fs_servers */ + struct list_head probe_link; /* Link in net->fs_probe_list */ struct hlist_node addr4_link; /* Link in net->fs_addresses4 */ struct hlist_node addr6_link; /* Link in net->fs_addresses6 */ struct hlist_node proc_link; /* Link in net->fs_proc */ @@ -504,8 +508,6 @@ struct afs_server { #define AFS_SERVER_FL_NOT_FOUND 2 /* VL server says no such server */ #define AFS_SERVER_FL_VL_FAIL 3 /* Failed to access VL server */ #define AFS_SERVER_FL_UPDATING 4 -#define AFS_SERVER_FL_PROBED 5 /* The fileserver has been probed */ -#define AFS_SERVER_FL_PROBING 6 /* Fileserver is being probed */ #define AFS_SERVER_FL_NO_IBULK 7 /* Fileserver doesn't support FS.InlineBulkStatus */ #define AFS_SERVER_FL_MAY_HAVE_CB 8 /* May have callbacks on this fileserver */ #define AFS_SERVER_FL_IS_YFS 9 /* Server is YFS not AFS */ @@ -527,6 +529,7 @@ struct afs_server { rwlock_t cb_break_lock; /* Volume finding lock */ /* Probe state */ + unsigned long probed_at; /* Time last probe was dispatched (jiffies) */ wait_queue_head_t probe_wq; atomic_t probe_outstanding; spinlock_t probe_lock; @@ -956,7 +959,6 @@ extern int afs_flock(struct file *, int, struct file_lock *); */ extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_status_cb *, struct afs_volsync *); -extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *); extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_status_cb *, struct afs_read *); extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t, struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); @@ -978,9 +980,8 @@ extern int afs_fs_extend_lock(struct afs_fs_cursor *, struct afs_status_cb *); extern int afs_fs_release_lock(struct afs_fs_cursor *, struct afs_status_cb *); extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *, struct afs_addr_cursor *, struct key *); -extern struct afs_call *afs_fs_get_capabilities(struct afs_net *, struct afs_server *, - struct afs_addr_cursor *, struct key *, - unsigned int); +extern bool afs_fs_get_capabilities(struct afs_net *, struct afs_server *, + struct afs_addr_cursor *, struct key *); extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *, struct afs_fid *, struct afs_status_cb *, unsigned int, struct afs_volsync *); @@ -1001,8 +1002,9 @@ extern int afs_fs_store_acl(struct afs_fs_cursor *, const struct afs_acl *, * fs_probe.c */ extern void afs_fileserver_probe_result(struct afs_call *); -extern int afs_probe_fileservers(struct afs_net *, struct key *, struct afs_server_list *); +extern void afs_fs_probe_fileserver(struct afs_net *, struct afs_server *, struct key *, bool); extern int afs_wait_for_fs_probes(struct afs_server_list *, unsigned long); +extern void afs_fs_probe_dispatcher(struct work_struct *); /* * inode.c @@ -1251,9 +1253,26 @@ extern void afs_unuse_server_notime(struct afs_net *, struct afs_server *, enum extern void afs_put_server(struct afs_net *, struct afs_server *, enum afs_server_trace); extern void afs_manage_servers(struct work_struct *); extern void afs_servers_timer(struct timer_list *); +extern void afs_fs_probe_timer(struct timer_list *); extern void __net_exit afs_purge_servers(struct afs_net *); extern bool afs_check_server_record(struct afs_fs_cursor *, struct afs_server *); +static inline void afs_inc_servers_outstanding(struct afs_net *net) +{ + atomic_inc(&net->servers_outstanding); +} + +static inline void afs_dec_servers_outstanding(struct afs_net *net) +{ + if (atomic_dec_and_test(&net->servers_outstanding)) + wake_up_var(&net->servers_outstanding); +} + +static inline bool afs_is_probing_server(struct afs_server *server) +{ + return list_empty(&server->probe_link); +} + /* * server_list.c */ diff --git a/fs/afs/main.c b/fs/afs/main.c index c9c45d7078bd..56b52f8dbf15 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c @@ -87,7 +87,8 @@ static int __net_init afs_net_init(struct net *net_ns) seqlock_init(&net->fs_lock); net->fs_servers = RB_ROOT; - INIT_LIST_HEAD(&net->fs_updates); + INIT_LIST_HEAD(&net->fs_probe_fast); + INIT_LIST_HEAD(&net->fs_probe_slow); INIT_HLIST_HEAD(&net->fs_proc); INIT_HLIST_HEAD(&net->fs_addresses4); @@ -96,6 +97,8 @@ static int __net_init afs_net_init(struct net *net_ns) INIT_WORK(&net->fs_manager, afs_manage_servers); timer_setup(&net->fs_timer, afs_servers_timer, 0); + INIT_WORK(&net->fs_prober, afs_fs_probe_dispatcher); + timer_setup(&net->fs_probe_timer, afs_fs_probe_timer, 0); ret = -ENOMEM; sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL); diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index 2a3305e42b14..46b68da89faa 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -349,9 +349,6 @@ start: goto failed; _debug("__ VOL %llx __", vnode->volume->vid); - error = afs_probe_fileservers(afs_v2net(vnode), fc->key, fc->server_list); - if (error < 0) - goto failed_set_error; pick_server: _debug("pick [%lx]", fc->untried); @@ -596,8 +593,8 @@ static void afs_dump_edestaddrreq(const struct afs_fs_cursor *fc) a->version, a->nr_ipv4, a->nr_addrs, a->max_addrs, a->preferred); - pr_notice("FC: - pr=%lx R=%lx F=%lx\n", - a->probed, a->responded, a->failed); + pr_notice("FC: - R=%lx F=%lx\n", + a->responded, a->failed); if (a == fc->ac.alist) pr_notice("FC: - current\n"); } diff --git a/fs/afs/server.c b/fs/afs/server.c index 4969a681f8f5..3f707b5ecb62 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -14,17 +14,6 @@ static unsigned afs_server_gc_delay = 10; /* Server record timeout in seconds */ static atomic_t afs_server_debug_id; -static void afs_inc_servers_outstanding(struct afs_net *net) -{ - atomic_inc(&net->servers_outstanding); -} - -static void afs_dec_servers_outstanding(struct afs_net *net) -{ - if (atomic_dec_and_test(&net->servers_outstanding)) - wake_up_var(&net->servers_outstanding); -} - static struct afs_server *afs_maybe_use_server(struct afs_server *, enum afs_server_trace); static void __afs_put_server(struct afs_net *, struct afs_server *); @@ -226,6 +215,7 @@ static struct afs_server *afs_alloc_server(struct afs_net *net, INIT_HLIST_HEAD(&server->cb_volumes); rwlock_init(&server->cb_break_lock); init_waitqueue_head(&server->probe_wq); + INIT_LIST_HEAD(&server->probe_link); spin_lock_init(&server->probe_lock); afs_inc_servers_outstanding(net); @@ -295,6 +285,12 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key, if (server != candidate) { afs_put_addrlist(alist); kfree(candidate); + } else { + /* Immediately dispatch an asynchronous probe to each interface + * on the fileserver. This will make sure the repeat-probing + * service is started. + */ + afs_fs_probe_fileserver(cell->net, server, key, true); } return server; @@ -464,6 +460,7 @@ static void afs_gc_servers(struct afs_net *net, struct afs_server *gc_list) trace_afs_server(server, atomic_read(&server->ref), active, afs_server_trace_gc); rb_erase(&server->uuid_rb, &net->fs_servers); + list_del(&server->probe_link); hlist_del_rcu(&server->proc_link); if (!hlist_unhashed(&server->addr4_link)) hlist_del_rcu(&server->addr4_link); diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 4310336b9bb8..249000195f8a 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -266,7 +266,6 @@ static int afs_update_volume_status(struct afs_volume *volume, struct key *key) } volume->update_at = ktime_get_real_seconds() + afs_volume_record_life; - clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags); write_unlock(&volume->servers_lock); ret = 0; @@ -283,23 +282,25 @@ error: */ int afs_check_volume_status(struct afs_volume *volume, struct afs_fs_cursor *fc) { - time64_t now = ktime_get_real_seconds(); int ret, retries = 0; _enter(""); - if (volume->update_at <= now) - set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags); - retry: - if (!test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags) && - !test_bit(AFS_VOLUME_WAIT, &volume->flags)) { - _leave(" = 0"); - return 0; - } - + if (test_bit(AFS_VOLUME_WAIT, &volume->flags)) + goto wait; + if (volume->update_at <= ktime_get_real_seconds() || + test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags)) + goto update; + _leave(" = 0"); + return 0; + +update: if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) { + clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags); ret = afs_update_volume_status(volume, fc->key); + if (ret < 0) + set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags); clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags); clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags); wake_up_bit(&volume->flags, AFS_VOLUME_WAIT); @@ -307,6 +308,7 @@ retry: return ret; } +wait: if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) { _leave(" = 0 [no wait]"); return 0; diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index f9691f69b2d6..19a07fbf35df 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -38,10 +38,12 @@ enum afs_server_trace { afs_server_trace_get_caps, afs_server_trace_get_install, afs_server_trace_get_new_cbi, + afs_server_trace_get_probe, afs_server_trace_give_up_cb, afs_server_trace_put_call, afs_server_trace_put_cbi, afs_server_trace_put_find_rsq, + afs_server_trace_put_probe, afs_server_trace_put_slist, afs_server_trace_put_slist_isort, afs_server_trace_put_uuid_rsq, @@ -247,10 +249,12 @@ enum afs_cb_break_reason { EM(afs_server_trace_get_caps, "GET caps ") \ EM(afs_server_trace_get_install, "GET inst ") \ EM(afs_server_trace_get_new_cbi, "GET cbi ") \ + EM(afs_server_trace_get_probe, "GET probe") \ EM(afs_server_trace_give_up_cb, "giveup-cb") \ EM(afs_server_trace_put_call, "PUT call ") \ EM(afs_server_trace_put_cbi, "PUT cbi ") \ EM(afs_server_trace_put_find_rsq, "PUT f-rsq") \ + EM(afs_server_trace_put_probe, "PUT probe") \ EM(afs_server_trace_put_slist, "PUT slist") \ EM(afs_server_trace_put_slist_isort, "PUT isort") \ EM(afs_server_trace_put_uuid_rsq, "PUT u-req") \ -- cgit v1.2.3-70-g09d2 From 6d043a578265e8c24384648f9c74c8874b429f28 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 20 Apr 2020 22:34:12 +0100 Subject: afs: Show more information in /proc/net/afs/servers Show more information in /proc/net/afs/servers to make it easier to see what's going on with the server probing. Signed-off-by: David Howells --- fs/afs/proc.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 9bce7898cd7d..1d21465a4108 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -378,21 +378,22 @@ static int afs_proc_servers_show(struct seq_file *m, void *v) int i; if (v == SEQ_START_TOKEN) { - seq_puts(m, "UUID REF ACT ADDR\n"); + seq_puts(m, "UUID REF ACT\n"); return 0; } server = list_entry(v, struct afs_server, proc_link); alist = rcu_dereference(server->addresses); - seq_printf(m, "%pU %3d %3d %pISpc%s\n", + seq_printf(m, "%pU %3d %3d\n", &server->uuid, atomic_read(&server->ref), - atomic_read(&server->active), - &alist->addrs[0].transport, - alist->preferred == 0 ? "*" : ""); - for (i = 1; i < alist->nr_addrs; i++) - seq_printf(m, " %pISpc%s\n", - &alist->addrs[i].transport, + atomic_read(&server->active)); + seq_printf(m, " - ALIST v=%u osp=%u r=%lx f=%lx\n", + alist->version, atomic_read(&server->probe_outstanding), + alist->responded, alist->failed); + for (i = 0; i < alist->nr_addrs; i++) + seq_printf(m, " [%x] %pISpc%s\n", + i, &alist->addrs[i].transport, alist->preferred == i ? "*" : ""); return 0; } -- cgit v1.2.3-70-g09d2 From 8230fd8217b7ea76f838ae88e4a5a8e54f37099f Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 27 Mar 2020 15:02:44 +0000 Subject: afs: Make callback processing more efficient. afs_vol_interest objects represent the volume IDs currently being accessed from a fileserver. These hold lists of afs_cb_interest objects that repesent the superblocks using that volume ID on that server. When a callback notification from the server telling of a modification by another client arrives, the volume ID specified in the notification is looked up in the server's afs_vol_interest list. Through the afs_cb_interest list, the relevant superblocks can be iterated over and the specific inode looked up and marked in each one. Make the following efficiency improvements: (1) Hold rcu_read_lock() over the entire processing rather than locking it each time. (2) Do all the callbacks for each vid together rather than individually. Each volume then only needs to be looked up once. (3) afs_vol_interest objects are now stored in an rb_tree rather than a flat list to reduce the lookup step count. (4) afs_vol_interest lookup is now done with RCU, but because it's in an rb_tree which may rotate under us, a seqlock is used so that if it changes during the walk, we repeat the walk with a lock held. With this and the preceding patch which adds RCU-based lookups in the inode cache, target volumes/vnodes can be taken without the need to take any locks, except on the target itself. Signed-off-by: David Howells --- fs/afs/callback.c | 150 ++++++++++++++++++++++++++++++++++-------------------- fs/afs/internal.h | 6 +-- fs/afs/server.c | 4 +- 3 files changed, 100 insertions(+), 60 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 0dcbd40732d1..b16781e1683e 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -28,7 +28,7 @@ static struct afs_cb_interest *afs_create_interest(struct afs_server *server, { struct afs_vol_interest *new_vi, *vi; struct afs_cb_interest *new; - struct hlist_node **pp; + struct rb_node *parent, **pp; new_vi = kzalloc(sizeof(struct afs_vol_interest), GFP_KERNEL); if (!new_vi) @@ -42,7 +42,6 @@ static struct afs_cb_interest *afs_create_interest(struct afs_server *server, new_vi->usage = 1; new_vi->vid = vnode->volume->vid; - INIT_HLIST_NODE(&new_vi->srv_link); INIT_HLIST_HEAD(&new_vi->cb_interests); refcount_set(&new->usage, 1); @@ -51,31 +50,31 @@ static struct afs_cb_interest *afs_create_interest(struct afs_server *server, new->server = afs_get_server(server, afs_server_trace_get_new_cbi); INIT_HLIST_NODE(&new->cb_vlink); - write_lock(&server->cb_break_lock); + write_seqlock(&server->cb_break_lock); - for (pp = &server->cb_volumes.first; *pp; pp = &(*pp)->next) { - vi = hlist_entry(*pp, struct afs_vol_interest, srv_link); - if (vi->vid < new_vi->vid) - continue; - if (vi->vid > new_vi->vid) - break; - vi->usage++; - goto found_vi; + pp = &server->cb_volumes.rb_node; + while ((parent = *pp)) { + vi = rb_entry(parent, struct afs_vol_interest, srv_node); + if (vi->vid < new_vi->vid) { + pp = &(*pp)->rb_left; + } else if (vi->vid > new_vi->vid) { + pp = &(*pp)->rb_right; + } else { + vi->usage++; + goto found_vi; + } } - new_vi->srv_link.pprev = pp; - new_vi->srv_link.next = *pp; - if (*pp) - (*pp)->pprev = &new_vi->srv_link.next; - *pp = &new_vi->srv_link; vi = new_vi; new_vi = NULL; -found_vi: + rb_link_node_rcu(&vi->srv_node, parent, pp); + rb_insert_color(&vi->srv_node, &server->cb_volumes); +found_vi: new->vol_interest = vi; hlist_add_head(&new->cb_vlink, &vi->cb_interests); - write_unlock(&server->cb_break_lock); + write_sequnlock(&server->cb_break_lock); kfree(new_vi); return new; } @@ -182,17 +181,17 @@ void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) if (cbi && refcount_dec_and_test(&cbi->usage)) { if (!hlist_unhashed(&cbi->cb_vlink)) { - write_lock(&cbi->server->cb_break_lock); + write_seqlock(&cbi->server->cb_break_lock); hlist_del_init(&cbi->cb_vlink); vi = cbi->vol_interest; cbi->vol_interest = NULL; if (--vi->usage == 0) - hlist_del(&vi->srv_link); + rb_erase(&vi->srv_node, &cbi->server->cb_volumes); else vi = NULL; - write_unlock(&cbi->server->cb_break_lock); + write_sequnlock(&cbi->server->cb_break_lock); if (vi) kfree_rcu(vi, rcu); afs_put_server(net, cbi->server, afs_server_trace_put_cbi); @@ -237,6 +236,45 @@ void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason write_sequnlock(&vnode->cb_lock); } +/* + * Look up a volume interest by volume ID under RCU conditions. + */ +static struct afs_vol_interest *afs_lookup_vol_interest_rcu(struct afs_server *server, + afs_volid_t vid) +{ + struct afs_vol_interest *vi = NULL; + struct rb_node *p; + int seq = 0; + + do { + /* Unfortunately, rbtree walking doesn't give reliable results + * under just the RCU read lock, so we have to check for + * changes. + */ + read_seqbegin_or_lock(&server->cb_break_lock, &seq); + + p = rcu_dereference_raw(server->cb_volumes.rb_node); + while (p) { + vi = rb_entry(p, struct afs_vol_interest, srv_node); + + if (vi->vid < vid) + p = rcu_dereference_raw(p->rb_left); + else if (vi->vid > vid) + p = rcu_dereference_raw(p->rb_right); + else + break; + /* We want to repeat the search, this time with the + * lock properly locked. + */ + vi = NULL; + } + + } while (need_seqretry(&server->cb_break_lock, seq)); + + done_seqretry(&server->cb_break_lock, seq); + return vi; +} + /* * allow the fileserver to explicitly break one callback * - happens when @@ -244,37 +282,18 @@ void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason * - a lock is released */ static void afs_break_one_callback(struct afs_server *server, - struct afs_fid *fid) + struct afs_fid *fid, + struct afs_vol_interest *vi) { - struct afs_vol_interest *vi; struct afs_cb_interest *cbi; struct afs_iget_data data; struct afs_vnode *vnode; struct inode *inode; - rcu_read_lock(); - read_lock(&server->cb_break_lock); - hlist_for_each_entry(vi, &server->cb_volumes, srv_link) { - if (vi->vid < fid->vid) - continue; - if (vi->vid > fid->vid) { - vi = NULL; - break; - } - //atomic_inc(&vi->usage); - break; - } - - /* TODO: Find all matching volumes if we couldn't match the server and - * break them anyway. - */ - if (!vi) - goto out; - /* Step through all interested superblocks. There may be more than one * because of cell aliasing. */ - hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) { + hlist_for_each_entry_rcu(cbi, &vi->cb_interests, cb_vlink) { if (fid->vnode == 0 && fid->unique == 0) { /* The callback break applies to an entire volume. */ struct afs_super_info *as = AFS_FS_S(cbi->sb); @@ -303,10 +322,36 @@ static void afs_break_one_callback(struct afs_server *server, } } } +} -out: - read_unlock(&server->cb_break_lock); - rcu_read_unlock(); +static void afs_break_some_callbacks(struct afs_server *server, + struct afs_callback_break *cbb, + size_t *_count) +{ + struct afs_callback_break *residue = cbb; + struct afs_vol_interest *vi; + afs_volid_t vid = cbb->fid.vid; + size_t i; + + vi = afs_lookup_vol_interest_rcu(server, vid); + + /* TODO: Find all matching volumes if we couldn't match the server and + * break them anyway. + */ + + for (i = *_count; i > 0; cbb++, i--) { + if (cbb->fid.vid == vid) { + _debug("- Fid { vl=%08llx n=%llu u=%u }", + cbb->fid.vid, + cbb->fid.vnode, + cbb->fid.unique); + --*_count; + if (vi) + afs_break_one_callback(server, &cbb->fid, vi); + } else { + *residue++ = *cbb; + } + } } /* @@ -319,17 +364,12 @@ void afs_break_callbacks(struct afs_server *server, size_t count, ASSERT(server != NULL); - /* TODO: Sort the callback break list by volume ID */ + rcu_read_lock(); - for (; count > 0; callbacks++, count--) { - _debug("- Fid { vl=%08llx n=%llu u=%u }", - callbacks->fid.vid, - callbacks->fid.vnode, - callbacks->fid.unique); - afs_break_one_callback(server, &callbacks->fid); - } + while (count > 0) + afs_break_some_callbacks(server, callbacks, &count); - _leave(""); + rcu_read_unlock(); return; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 61320a632e15..b6665fc5d355 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -524,9 +524,9 @@ struct afs_server { rwlock_t fs_lock; /* access lock */ /* callback promise management */ - struct hlist_head cb_volumes; /* List of volume interests on this server */ + struct rb_root cb_volumes; /* List of volume interests on this server */ unsigned cb_s_break; /* Break-everything counter. */ - rwlock_t cb_break_lock; /* Volume finding lock */ + seqlock_t cb_break_lock; /* Volume finding lock */ /* Probe state */ unsigned long probed_at; /* Time last probe was dispatched (jiffies) */ @@ -552,7 +552,7 @@ struct afs_server { * Volume collation in the server's callback interest list. */ struct afs_vol_interest { - struct hlist_node srv_link; /* Link in server->cb_volumes */ + struct rb_node srv_node; /* Link in server->cb_volumes */ struct hlist_head cb_interests; /* List of callback interests on the server */ union { struct rcu_head rcu; diff --git a/fs/afs/server.c b/fs/afs/server.c index 3f707b5ecb62..5ed90f419c54 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -212,8 +212,8 @@ static struct afs_server *afs_alloc_server(struct afs_net *net, server->addr_version = alist->version; server->uuid = *uuid; rwlock_init(&server->fs_lock); - INIT_HLIST_HEAD(&server->cb_volumes); - rwlock_init(&server->cb_break_lock); + server->cb_volumes = RB_ROOT; + seqlock_init(&server->cb_break_lock); init_waitqueue_head(&server->probe_wq); INIT_LIST_HEAD(&server->probe_link); spin_lock_init(&server->probe_lock); -- cgit v1.2.3-70-g09d2 From 38355eec6a7d2b8f2f313f9174736dc877744e59 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2020 16:13:20 +0100 Subject: afs: Set error flag rather than return error from file status decode Set a flag in the call struct to indicate an unmarshalling error rather than return and handle an error from the decoding of file statuses. This flag is checked on a successful return from the delivery function. Signed-off-by: David Howells --- fs/afs/fsclient.c | 88 ++++++++++++++++-------------------------------------- fs/afs/internal.h | 1 + fs/afs/rxrpc.c | 4 +++ fs/afs/yfsclient.c | 85 ++++++++++++++++------------------------------------ 4 files changed, 55 insertions(+), 123 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 401de063996c..b1d8d8f780d2 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -56,16 +56,15 @@ static void xdr_dump_bad(const __be32 *bp) /* * decode an AFSFetchStatus block */ -static int xdr_decode_AFSFetchStatus(const __be32 **_bp, - struct afs_call *call, - struct afs_status_cb *scb) +static void xdr_decode_AFSFetchStatus(const __be32 **_bp, + struct afs_call *call, + struct afs_status_cb *scb) { const struct afs_xdr_AFSFetchStatus *xdr = (const void *)*_bp; struct afs_file_status *status = &scb->status; bool inline_error = (call->operation_ID == afs_FS_InlineBulkStatus); u64 data_version, size; u32 type, abort_code; - int ret; abort_code = ntohl(xdr->abort_code); @@ -79,7 +78,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, */ status->abort_code = abort_code; scb->have_error = true; - goto good; + goto advance; } pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version)); @@ -89,7 +88,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, if (abort_code != 0 && inline_error) { status->abort_code = abort_code; scb->have_error = true; - goto good; + goto advance; } type = ntohl(xdr->type); @@ -125,15 +124,13 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, data_version |= (u64)ntohl(xdr->data_version_hi) << 32; status->data_version = data_version; scb->have_status = true; -good: - ret = 0; advance: *_bp = (const void *)*_bp + sizeof(*xdr); - return ret; + return; bad: xdr_dump_bad(*_bp); - ret = afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status); + afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status); goto advance; } @@ -254,9 +251,7 @@ static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); xdr_decode_AFSCallBack(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); @@ -419,9 +414,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); xdr_decode_AFSCallBack(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); @@ -577,12 +570,8 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; xdr_decode_AFSFid(&bp, call->out_fid); - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); + xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_AFSCallBack(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); @@ -691,9 +680,7 @@ static int afs_deliver_fs_dir_status_and_vol(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); @@ -784,12 +771,8 @@ static int afs_deliver_fs_link(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); + xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); @@ -878,12 +861,8 @@ static int afs_deliver_fs_symlink(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; xdr_decode_AFSFid(&bp, call->out_fid); - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); + xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); @@ -986,16 +965,12 @@ static int afs_deliver_fs_rename(struct afs_call *call) if (ret < 0) return ret; + bp = call->buffer; /* If the two dirs are the same, we have two copies of the same status * report, so we just decode it twice. */ - bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); @@ -1103,9 +1078,7 @@ static int afs_deliver_fs_store_data(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); @@ -1283,9 +1256,7 @@ static int afs_deliver_fs_store_status(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); @@ -1952,9 +1923,7 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); xdr_decode_AFSCallBack(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); @@ -2060,10 +2029,7 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) bp = call->buffer; scb = &call->out_scb[call->count]; - ret = xdr_decode_AFSFetchStatus(&bp, call, scb); - if (ret < 0) - return ret; - + xdr_decode_AFSFetchStatus(&bp, call, scb); call->count++; if (call->count < call->count2) goto more_counts; @@ -2241,9 +2207,7 @@ static int afs_deliver_fs_fetch_acl(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); call->unmarshall++; @@ -2324,9 +2288,7 @@ static int afs_deliver_fs_file_status_and_vol(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); xdr_decode_AFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index b6665fc5d355..6d5c66dd76de 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -160,6 +160,7 @@ struct afs_call { bool upgrade; /* T to request service upgrade */ bool have_reply_time; /* T if have got reply_time */ bool intr; /* T if interruptible */ + bool unmarshalling_error; /* T if an unmarshalling error occurred */ u16 service_id; /* Actual service ID (after upgrade) */ unsigned int debug_id; /* Trace ID */ u32 operation_ID; /* operation ID for an incoming call */ diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index ab2962fff1fb..c84d571782d7 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -540,6 +540,8 @@ static void afs_deliver_to_call(struct afs_call *call) ret = call->type->deliver(call); state = READ_ONCE(call->state); + if (ret == 0 && call->unmarshalling_error) + ret = -EBADMSG; switch (ret) { case 0: afs_queue_call_work(call); @@ -963,5 +965,7 @@ noinline int afs_protocol_error(struct afs_call *call, int error, enum afs_eproto_cause cause) { trace_afs_protocol_error(call, error, cause); + if (call) + call->unmarshalling_error = true; return error; } diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index fe413e7a5cf4..f118daa5f33a 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -179,21 +179,20 @@ static void xdr_dump_bad(const __be32 *bp) /* * Decode a YFSFetchStatus block */ -static int xdr_decode_YFSFetchStatus(const __be32 **_bp, - struct afs_call *call, - struct afs_status_cb *scb) +static void xdr_decode_YFSFetchStatus(const __be32 **_bp, + struct afs_call *call, + struct afs_status_cb *scb) { const struct yfs_xdr_YFSFetchStatus *xdr = (const void *)*_bp; struct afs_file_status *status = &scb->status; u32 type; - int ret; status->abort_code = ntohl(xdr->abort_code); if (status->abort_code != 0) { if (status->abort_code == VNOVNODE) status->nlink = 0; scb->have_error = true; - goto good; + goto advance; } type = ntohl(xdr->type); @@ -221,15 +220,13 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp, status->size = xdr_to_u64(xdr->size); status->data_version = xdr_to_u64(xdr->data_version); scb->have_status = true; -good: - ret = 0; advance: *_bp += xdr_size(xdr); - return ret; + return; bad: xdr_dump_bad(*_bp); - ret = afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status); + afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status); goto advance; } @@ -348,9 +345,7 @@ static int yfs_deliver_fs_status_cb_and_volsync(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); xdr_decode_YFSCallBack(&bp, call, call->out_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); @@ -372,9 +367,7 @@ static int yfs_deliver_status_and_volsync(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); @@ -534,9 +527,7 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); xdr_decode_YFSCallBack(&bp, call, call->out_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); @@ -644,12 +635,8 @@ static int yfs_deliver_fs_create_vnode(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; xdr_decode_YFSFid(&bp, call->out_fid); - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); + xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_YFSCallBack(&bp, call, call->out_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); @@ -802,14 +789,9 @@ static int yfs_deliver_fs_remove_file2(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; - + xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_YFSFid(&bp, &fid); - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); /* Was deleted if vnode->status.abort_code == VNOVNODE. */ xdr_decode_YFSVolSync(&bp, call->out_volsync); @@ -889,10 +871,7 @@ static int yfs_deliver_fs_remove(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; - + xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); return 0; } @@ -974,12 +953,8 @@ static int yfs_deliver_fs_link(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); + xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); return 0; @@ -1061,12 +1036,8 @@ static int yfs_deliver_fs_symlink(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; xdr_decode_YFSFid(&bp, call->out_fid); - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); + xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); @@ -1154,13 +1125,11 @@ static int yfs_deliver_fs_rename(struct afs_call *call) return ret; bp = call->buffer; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - if (ret < 0) - return ret; - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; - + /* If the two dirs are the same, we have two copies of the same status + * report, so we just decode it twice. + */ + xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); _leave(" = 0 [done]"); return 0; @@ -1845,9 +1814,7 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) bp = call->buffer; scb = &call->out_scb[call->count]; - ret = xdr_decode_YFSFetchStatus(&bp, call, scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, scb); call->count++; if (call->count < call->count2) @@ -2067,9 +2034,7 @@ static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call) bp = call->buffer; yacl->inherit_flag = ntohl(*bp++); yacl->num_cleaned = ntohl(*bp++); - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - if (ret < 0) - return ret; + xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); xdr_decode_YFSVolSync(&bp, call->out_volsync); call->unmarshall++; -- cgit v1.2.3-70-g09d2 From 7126ead910aa9fcc9e16e9e7a8c9179658261f1d Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2020 16:49:08 +0100 Subject: afs: Remove the error argument from afs_protocol_error() Remove the error argument from afs_protocol_error() as it's always -EBADMSG. Signed-off-by: David Howells --- fs/afs/cmservice.c | 9 +++------ fs/afs/fsclient.c | 17 ++++++----------- fs/afs/inode.c | 4 ++-- fs/afs/internal.h | 2 +- fs/afs/rxrpc.c | 6 +++--- fs/afs/vlclient.c | 34 ++++++++++++++-------------------- fs/afs/yfsclient.c | 17 ++++++----------- include/trace/events/afs.h | 10 ++++------ 8 files changed, 39 insertions(+), 60 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 7ae88958051f..ed0fb34d77dd 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -307,8 +307,7 @@ static int afs_deliver_cb_callback(struct afs_call *call) call->count = ntohl(call->tmp); _debug("FID count: %u", call->count); if (call->count > AFSCBMAX) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_cb_fid_count); + return afs_protocol_error(call, afs_eproto_cb_fid_count); call->buffer = kmalloc(array3_size(call->count, 3, 4), GFP_KERNEL); @@ -353,8 +352,7 @@ static int afs_deliver_cb_callback(struct afs_call *call) call->count2 = ntohl(call->tmp); _debug("CB count: %u", call->count2); if (call->count2 != call->count && call->count2 != 0) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_cb_count); + return afs_protocol_error(call, afs_eproto_cb_count); call->iter = &call->def_iter; iov_iter_discard(&call->def_iter, READ, call->count2 * 3 * 4); call->unmarshall++; @@ -674,8 +672,7 @@ static int afs_deliver_yfs_cb_callback(struct afs_call *call) call->count = ntohl(call->tmp); _debug("FID count: %u", call->count); if (call->count > YFSCBMAX) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_cb_fid_count); + return afs_protocol_error(call, afs_eproto_cb_fid_count); size = array_size(call->count, sizeof(struct yfs_xdr_YFSFid)); call->buffer = kmalloc(size, GFP_KERNEL); diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index b1d8d8f780d2..7d4503174dd1 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -130,7 +130,7 @@ advance: bad: xdr_dump_bad(*_bp); - afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status); + afs_protocol_error(call, afs_eproto_bad_status); goto advance; } @@ -1470,8 +1470,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call) call->count = ntohl(call->tmp); _debug("volname length: %u", call->count); if (call->count >= AFSNAMEMAX) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_volname_len); + return afs_protocol_error(call, afs_eproto_volname_len); size = (call->count + 3) & ~3; /* It's padded */ afs_extract_to_buf(call, size); call->unmarshall++; @@ -1500,8 +1499,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call) call->count = ntohl(call->tmp); _debug("offline msg length: %u", call->count); if (call->count >= AFSNAMEMAX) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_offline_msg_len); + return afs_protocol_error(call, afs_eproto_offline_msg_len); size = (call->count + 3) & ~3; /* It's padded */ afs_extract_to_buf(call, size); call->unmarshall++; @@ -1531,8 +1529,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call) call->count = ntohl(call->tmp); _debug("motd length: %u", call->count); if (call->count >= AFSNAMEMAX) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_motd_len); + return afs_protocol_error(call, afs_eproto_motd_len); size = (call->count + 3) & ~3; /* It's padded */ afs_extract_to_buf(call, size); call->unmarshall++; @@ -2012,8 +2009,7 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) tmp = ntohl(call->tmp); _debug("status count: %u/%u", tmp, call->count2); if (tmp != call->count2) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_ibulkst_count); + return afs_protocol_error(call, afs_eproto_ibulkst_count); call->count = 0; call->unmarshall++; @@ -2049,8 +2045,7 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) tmp = ntohl(call->tmp); _debug("CB count: %u", tmp); if (tmp != call->count2) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_ibulkst_cb_count); + return afs_protocol_error(call, afs_eproto_ibulkst_cb_count); call->count = 0; call->unmarshall++; more_cbs: diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 281470fe1183..07933d106e0e 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -130,7 +130,7 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key, default: dump_vnode(vnode, parent_vnode); write_sequnlock(&vnode->cb_lock); - return afs_protocol_error(NULL, -EBADMSG, afs_eproto_file_type); + return afs_protocol_error(NULL, afs_eproto_file_type); } afs_set_i_size(vnode, status->size); @@ -179,7 +179,7 @@ static void afs_apply_status(struct afs_fs_cursor *fc, vnode->fid.vnode, vnode->fid.unique, status->type, vnode->status.type); - afs_protocol_error(NULL, -EBADMSG, afs_eproto_bad_status); + afs_protocol_error(NULL, afs_eproto_bad_status); return; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 6d5c66dd76de..468bd2b0470d 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1133,7 +1133,7 @@ extern void afs_flat_call_destructor(struct afs_call *); extern void afs_send_empty_reply(struct afs_call *); extern void afs_send_simple_reply(struct afs_call *, const void *, size_t); extern int afs_extract_data(struct afs_call *, bool); -extern int afs_protocol_error(struct afs_call *, int, enum afs_eproto_cause); +extern int afs_protocol_error(struct afs_call *, enum afs_eproto_cause); static inline void afs_set_fc_call(struct afs_call *call, struct afs_fs_cursor *fc) { diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index c84d571782d7..00b87bac4fec 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -961,11 +961,11 @@ int afs_extract_data(struct afs_call *call, bool want_more) /* * Log protocol error production. */ -noinline int afs_protocol_error(struct afs_call *call, int error, +noinline int afs_protocol_error(struct afs_call *call, enum afs_eproto_cause cause) { - trace_afs_protocol_error(call, error, cause); + trace_afs_protocol_error(call, cause); if (call) call->unmarshalling_error = true; - return error; + return -EBADMSG; } diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c index 972dc5512f33..d0c85623ce8f 100644 --- a/fs/afs/vlclient.c +++ b/fs/afs/vlclient.c @@ -448,8 +448,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) call->count2 = ntohl(*bp); /* Type or next count */ if (call->count > YFS_MAXENDPOINTS) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_fsendpt_num); + return afs_protocol_error(call, afs_eproto_yvl_fsendpt_num); alist = afs_alloc_addrlist(call->count, FS_SERVICE, AFS_FS_PORT); if (!alist) @@ -469,8 +468,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) size = sizeof(__be32) * (1 + 4 + 1); break; default: - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_fsendpt_type); + return afs_protocol_error(call, afs_eproto_yvl_fsendpt_type); } size += sizeof(__be32); @@ -488,21 +486,20 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) switch (call->count2) { case YFS_ENDPOINT_IPV4: if (ntohl(bp[0]) != sizeof(__be32) * 2) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_fsendpt4_len); + return afs_protocol_error( + call, afs_eproto_yvl_fsendpt4_len); afs_merge_fs_addr4(alist, bp[1], ntohl(bp[2])); bp += 3; break; case YFS_ENDPOINT_IPV6: if (ntohl(bp[0]) != sizeof(__be32) * 5) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_fsendpt6_len); + return afs_protocol_error( + call, afs_eproto_yvl_fsendpt6_len); afs_merge_fs_addr6(alist, bp + 1, ntohl(bp[5])); bp += 6; break; default: - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_fsendpt_type); + return afs_protocol_error(call, afs_eproto_yvl_fsendpt_type); } /* Got either the type of the next entry or the count of @@ -520,8 +517,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) if (!call->count) goto end; if (call->count > YFS_MAXENDPOINTS) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_vlendpt_type); + return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type); afs_extract_to_buf(call, 1 * sizeof(__be32)); call->unmarshall = 3; @@ -548,8 +544,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) size = sizeof(__be32) * (1 + 4 + 1); break; default: - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_vlendpt_type); + return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type); } if (call->count > 1) @@ -567,19 +562,18 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) switch (call->count2) { case YFS_ENDPOINT_IPV4: if (ntohl(bp[0]) != sizeof(__be32) * 2) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_vlendpt4_len); + return afs_protocol_error( + call, afs_eproto_yvl_vlendpt4_len); bp += 3; break; case YFS_ENDPOINT_IPV6: if (ntohl(bp[0]) != sizeof(__be32) * 5) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_vlendpt6_len); + return afs_protocol_error( + call, afs_eproto_yvl_vlendpt6_len); bp += 6; break; default: - return afs_protocol_error(call, -EBADMSG, - afs_eproto_yvl_vlendpt_type); + return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type); } /* Got either the type of the next entry or the count of diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index f118daa5f33a..bf74c679c02b 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -226,7 +226,7 @@ advance: bad: xdr_dump_bad(*_bp); - afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status); + afs_protocol_error(call, afs_eproto_bad_status); goto advance; } @@ -1426,8 +1426,7 @@ static int yfs_deliver_fs_get_volume_status(struct afs_call *call) call->count = ntohl(call->tmp); _debug("volname length: %u", call->count); if (call->count >= AFSNAMEMAX) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_volname_len); + return afs_protocol_error(call, afs_eproto_volname_len); size = (call->count + 3) & ~3; /* It's padded */ afs_extract_to_buf(call, size); call->unmarshall++; @@ -1456,8 +1455,7 @@ static int yfs_deliver_fs_get_volume_status(struct afs_call *call) call->count = ntohl(call->tmp); _debug("offline msg length: %u", call->count); if (call->count >= AFSNAMEMAX) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_offline_msg_len); + return afs_protocol_error(call, afs_eproto_offline_msg_len); size = (call->count + 3) & ~3; /* It's padded */ afs_extract_to_buf(call, size); call->unmarshall++; @@ -1487,8 +1485,7 @@ static int yfs_deliver_fs_get_volume_status(struct afs_call *call) call->count = ntohl(call->tmp); _debug("motd length: %u", call->count); if (call->count >= AFSNAMEMAX) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_motd_len); + return afs_protocol_error(call, afs_eproto_motd_len); size = (call->count + 3) & ~3; /* It's padded */ afs_extract_to_buf(call, size); call->unmarshall++; @@ -1797,8 +1794,7 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) tmp = ntohl(call->tmp); _debug("status count: %u/%u", tmp, call->count2); if (tmp != call->count2) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_ibulkst_count); + return afs_protocol_error(call, afs_eproto_ibulkst_count); call->count = 0; call->unmarshall++; @@ -1835,8 +1831,7 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) tmp = ntohl(call->tmp); _debug("CB count: %u", tmp); if (tmp != call->count2) - return afs_protocol_error(call, -EBADMSG, - afs_eproto_ibulkst_cb_count); + return afs_protocol_error(call, afs_eproto_ibulkst_cb_count); call->count = 0; call->unmarshall++; more_cbs: diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index 19a07fbf35df..a6d8a9891164 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -994,24 +994,22 @@ TRACE_EVENT(afs_edit_dir, ); TRACE_EVENT(afs_protocol_error, - TP_PROTO(struct afs_call *call, int error, enum afs_eproto_cause cause), + TP_PROTO(struct afs_call *call, enum afs_eproto_cause cause), - TP_ARGS(call, error, cause), + TP_ARGS(call, cause), TP_STRUCT__entry( __field(unsigned int, call ) - __field(int, error ) __field(enum afs_eproto_cause, cause ) ), TP_fast_assign( __entry->call = call ? call->debug_id : 0; - __entry->error = error; __entry->cause = cause; ), - TP_printk("c=%08x r=%d %s", - __entry->call, __entry->error, + TP_printk("c=%08x %s", + __entry->call, __print_symbolic(__entry->cause, afs_eproto_causes)) ); -- cgit v1.2.3-70-g09d2 From a310082f6d0afe28797e148726cd52118a8a4428 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 20 Mar 2020 09:32:50 +0000 Subject: afs: Rename struct afs_fs_cursor to afs_operation As a prelude to implementing asynchronous fileserver operations in the afs filesystem, rename struct afs_fs_cursor to afs_operation. This struct is going to form the core of the operation management and is going to acquire more members in later. Signed-off-by: David Howells --- fs/afs/dir.c | 22 ++-- fs/afs/dir_silly.c | 4 +- fs/afs/file.c | 2 +- fs/afs/flock.c | 6 +- fs/afs/fsclient.c | 42 ++++---- fs/afs/inode.c | 10 +- fs/afs/internal.h | 112 ++++++++++---------- fs/afs/rotate.c | 292 ++++++++++++++++++++++++++--------------------------- fs/afs/server.c | 8 +- fs/afs/super.c | 4 +- fs/afs/volume.c | 4 +- fs/afs/write.c | 2 +- fs/afs/xattr.c | 8 +- fs/afs/yfsclient.c | 40 ++++---- 14 files changed, 278 insertions(+), 278 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 3c486340b220..ff421db40cf2 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -643,7 +643,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, struct afs_super_info *as = dir->i_sb->s_fs_info; struct afs_status_cb *scb; struct afs_iget_data iget_data; - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_server *server; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; struct inode *inode = NULL, *ti; @@ -1220,7 +1220,7 @@ void afs_d_release(struct dentry *dentry) /* * Create a new inode for create/mkdir/symlink */ -static void afs_vnode_new_inode(struct afs_fs_cursor *fc, +static void afs_vnode_new_inode(struct afs_operation *fc, struct dentry *new_dentry, struct afs_iget_data *new_data, struct afs_status_cb *new_scb) @@ -1248,7 +1248,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc, d_instantiate(new_dentry, inode); } -static void afs_prep_for_new_inode(struct afs_fs_cursor *fc, +static void afs_prep_for_new_inode(struct afs_operation *fc, struct afs_iget_data *iget_data) { iget_data->volume = fc->vnode->volume; @@ -1261,7 +1261,7 @@ static void afs_prep_for_new_inode(struct afs_fs_cursor *fc, * number derived from the result of the operation. It doesn't matter if * d_fsdata goes backwards as we'll just revalidate. */ -static void afs_update_dentry_version(struct afs_fs_cursor *fc, +static void afs_update_dentry_version(struct afs_operation *fc, struct dentry *dentry, struct afs_status_cb *scb) { @@ -1277,7 +1277,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { struct afs_iget_data iget_data; struct afs_status_cb *scb; - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_vnode *dvnode = AFS_FS_I(dir); struct key *key; afs_dataversion_t data_version; @@ -1367,7 +1367,7 @@ static void afs_dir_remove_subdir(struct dentry *dentry) static int afs_rmdir(struct inode *dir, struct dentry *dentry) { struct afs_status_cb *scb; - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL; struct key *key; afs_dataversion_t data_version; @@ -1483,7 +1483,7 @@ static int afs_dir_remove_link(struct afs_vnode *dvnode, struct dentry *dentry, */ static int afs_unlink(struct inode *dir, struct dentry *dentry) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *dvnode = AFS_FS_I(dir); struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); @@ -1588,7 +1588,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct afs_iget_data iget_data; - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *dvnode = AFS_FS_I(dir); struct key *key; @@ -1666,7 +1666,7 @@ error: static int afs_link(struct dentry *from, struct inode *dir, struct dentry *dentry) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *dvnode = AFS_FS_I(dir); struct afs_vnode *vnode = AFS_FS_I(d_inode(from)); @@ -1755,7 +1755,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, const char *content) { struct afs_iget_data iget_data; - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *dvnode = AFS_FS_I(dir); struct key *key; @@ -1837,7 +1837,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; struct dentry *tmp = NULL, *rehash = NULL; diff --git a/fs/afs/dir_silly.c b/fs/afs/dir_silly.c index d94e2b7cddff..0a82b134aa0d 100644 --- a/fs/afs/dir_silly.c +++ b/fs/afs/dir_silly.c @@ -19,7 +19,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode struct dentry *old, struct dentry *new, struct key *key) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; afs_dataversion_t dir_data_version; int ret = -ERESTARTSYS; @@ -145,7 +145,7 @@ out: 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; + struct afs_operation fc; struct afs_status_cb *scb; int ret = -ERESTARTSYS; diff --git a/fs/afs/file.c b/fs/afs/file.c index 8415733f7bc1..0c0ccc1412ee 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -225,7 +225,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 *req) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; int ret; diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 0f2a94ba73cb..682fe745f10e 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c @@ -179,7 +179,7 @@ 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; + struct afs_operation fc; int ret; _enter("%s{%llx:%llu.%u},%x,%u", @@ -216,7 +216,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; + struct afs_operation fc; int ret; _enter("%s{%llx:%llu.%u},%x", @@ -253,7 +253,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; + struct afs_operation fc; int ret; _enter("%s{%llx:%llu.%u},%x", diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 7d4503174dd1..3e423e9daa24 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -272,7 +272,7 @@ static const struct afs_call_type afs_RXFSFetchStatus_vnode = { /* * fetch the status information for a file */ -int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_status_cb *scb, +int afs_fs_fetch_file_status(struct afs_operation *fc, struct afs_status_cb *scb, struct afs_volsync *volsync) { struct afs_vnode *vnode = fc->vnode; @@ -470,7 +470,7 @@ static const struct afs_call_type afs_RXFSFetchData64 = { /* * fetch data from a very large file */ -static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, +static int afs_fs_fetch_data64(struct afs_operation *fc, struct afs_status_cb *scb, struct afs_read *req) { @@ -511,7 +511,7 @@ static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, /* * fetch data from a file */ -int afs_fs_fetch_data(struct afs_fs_cursor *fc, +int afs_fs_fetch_data(struct afs_operation *fc, struct afs_status_cb *scb, struct afs_read *req) { @@ -599,7 +599,7 @@ static const struct afs_call_type afs_RXFSMakeDir = { /* * create a file or make a directory */ -int afs_fs_create(struct afs_fs_cursor *fc, +int afs_fs_create(struct afs_operation *fc, const char *name, umode_t mode, struct afs_status_cb *dvnode_scb, @@ -707,7 +707,7 @@ static const struct afs_call_type afs_RXFSRemoveDir = { /* * remove a file or directory */ -int afs_fs_remove(struct afs_fs_cursor *fc, struct afs_vnode *vnode, +int afs_fs_remove(struct afs_operation *fc, struct afs_vnode *vnode, const char *name, bool isdir, struct afs_status_cb *dvnode_scb) { struct afs_vnode *dvnode = fc->vnode; @@ -792,7 +792,7 @@ static const struct afs_call_type afs_RXFSLink = { /* * make a hard link */ -int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, +int afs_fs_link(struct afs_operation *fc, struct afs_vnode *vnode, const char *name, struct afs_status_cb *dvnode_scb, struct afs_status_cb *vnode_scb) @@ -882,7 +882,7 @@ static const struct afs_call_type afs_RXFSSymlink = { /* * create a symbolic link */ -int afs_fs_symlink(struct afs_fs_cursor *fc, +int afs_fs_symlink(struct afs_operation *fc, const char *name, const char *contents, struct afs_status_cb *dvnode_scb, @@ -990,7 +990,7 @@ static const struct afs_call_type afs_RXFSRename = { /* * Rename/move a file or directory. */ -int afs_fs_rename(struct afs_fs_cursor *fc, +int afs_fs_rename(struct afs_operation *fc, const char *orig_name, struct afs_vnode *new_dvnode, const char *new_name, @@ -1105,7 +1105,7 @@ static const struct afs_call_type afs_RXFSStoreData64 = { /* * store a set of pages to a very large file */ -static int afs_fs_store_data64(struct afs_fs_cursor *fc, +static int afs_fs_store_data64(struct afs_operation *fc, struct address_space *mapping, pgoff_t first, pgoff_t last, unsigned offset, unsigned to, @@ -1165,7 +1165,7 @@ static int afs_fs_store_data64(struct afs_fs_cursor *fc, /* * store a set of pages */ -int afs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping, +int afs_fs_store_data(struct afs_operation *fc, struct address_space *mapping, pgoff_t first, pgoff_t last, unsigned offset, unsigned to, struct afs_status_cb *scb) @@ -1291,7 +1291,7 @@ static const struct afs_call_type afs_RXFSStoreData64_as_Status = { * set the attributes on a very large file, using FS.StoreData rather than * FS.StoreStatus so as to alter the file size also */ -static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct iattr *attr, +static int afs_fs_setattr_size64(struct afs_operation *fc, struct iattr *attr, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; @@ -1340,7 +1340,7 @@ static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct iattr *attr, * set the attributes on a file, using FS.StoreData rather than FS.StoreStatus * so as to alter the file size also */ -static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr, +static int afs_fs_setattr_size(struct afs_operation *fc, struct iattr *attr, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; @@ -1388,7 +1388,7 @@ static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr, * set the attributes on a file, using FS.StoreData if there's a change in file * size, and FS.StoreStatus otherwise */ -int afs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr, +int afs_fs_setattr(struct afs_operation *fc, struct iattr *attr, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; @@ -1569,7 +1569,7 @@ static const struct afs_call_type afs_RXFSGetVolumeStatus = { /* * fetch the status of a volume */ -int afs_fs_get_volume_status(struct afs_fs_cursor *fc, +int afs_fs_get_volume_status(struct afs_operation *fc, struct afs_volume_status *vs) { struct afs_vnode *vnode = fc->vnode; @@ -1659,7 +1659,7 @@ static const struct afs_call_type afs_RXFSReleaseLock = { /* * Set a lock on a file */ -int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type, +int afs_fs_set_lock(struct afs_operation *fc, afs_lock_type_t type, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; @@ -1698,7 +1698,7 @@ int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type, /* * extend a lock on a file */ -int afs_fs_extend_lock(struct afs_fs_cursor *fc, struct afs_status_cb *scb) +int afs_fs_extend_lock(struct afs_operation *fc, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; struct afs_call *call; @@ -1735,7 +1735,7 @@ int afs_fs_extend_lock(struct afs_fs_cursor *fc, struct afs_status_cb *scb) /* * release a lock on a file */ -int afs_fs_release_lock(struct afs_fs_cursor *fc, struct afs_status_cb *scb) +int afs_fs_release_lock(struct afs_operation *fc, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; struct afs_call *call; @@ -1941,7 +1941,7 @@ static const struct afs_call_type afs_RXFSFetchStatus = { /* * Fetch the status information for a fid without needing a vnode handle. */ -int afs_fs_fetch_status(struct afs_fs_cursor *fc, +int afs_fs_fetch_status(struct afs_operation *fc, struct afs_net *net, struct afs_fid *fid, struct afs_status_cb *scb, @@ -2101,7 +2101,7 @@ static const struct afs_call_type afs_RXFSInlineBulkStatus = { /* * Fetch the status information for up to 50 files */ -int afs_fs_inline_bulk_status(struct afs_fs_cursor *fc, +int afs_fs_inline_bulk_status(struct afs_operation *fc, struct afs_net *net, struct afs_fid *fids, struct afs_status_cb *statuses, @@ -2234,7 +2234,7 @@ static const struct afs_call_type afs_RXFSFetchACL = { /* * Fetch the ACL for a file. */ -struct afs_acl *afs_fs_fetch_acl(struct afs_fs_cursor *fc, +struct afs_acl *afs_fs_fetch_acl(struct afs_operation *fc, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; @@ -2303,7 +2303,7 @@ static const struct afs_call_type afs_RXFSStoreACL = { /* * Fetch the ACL for a file. */ -int afs_fs_store_acl(struct afs_fs_cursor *fc, const struct afs_acl *acl, +int afs_fs_store_acl(struct afs_operation *fc, const struct afs_acl *acl, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 07933d106e0e..d2dbb3aef611 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -161,7 +161,7 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key, /* * Update the core inode struct from a returned status record. */ -static void afs_apply_status(struct afs_fs_cursor *fc, +static void afs_apply_status(struct afs_operation *fc, struct afs_vnode *vnode, struct afs_status_cb *scb, const afs_dataversion_t *expected_version) @@ -243,7 +243,7 @@ static void afs_apply_status(struct afs_fs_cursor *fc, /* * Apply a callback to a vnode. */ -static void afs_apply_callback(struct afs_fs_cursor *fc, +static void afs_apply_callback(struct afs_operation *fc, struct afs_vnode *vnode, struct afs_status_cb *scb, unsigned int cb_break) @@ -267,7 +267,7 @@ static void afs_apply_callback(struct afs_fs_cursor *fc, * Apply the received status and callback to an inode all in the same critical * section to avoid races with afs_validate(). */ -void afs_vnode_commit_status(struct afs_fs_cursor *fc, +void afs_vnode_commit_status(struct afs_operation *fc, struct afs_vnode *vnode, unsigned int cb_break, const afs_dataversion_t *expected_version, @@ -304,7 +304,7 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool is_new, afs_access_t *_caller_access) { struct afs_status_cb *scb; - struct afs_fs_cursor fc; + struct afs_operation fc; int ret; _enter("%s,{%llx:%llu.%u,S=%lx}", @@ -813,7 +813,7 @@ void afs_evict_inode(struct inode *inode) */ int afs_setattr(struct dentry *dentry, struct iattr *attr) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); struct key *key; diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 468bd2b0470d..0551dedb0371 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -766,7 +766,7 @@ struct afs_vl_cursor { /* * Cursor for iterating over a set of fileservers. */ -struct afs_fs_cursor { +struct afs_operation { const struct afs_call_type *type; /* Type of call done */ struct afs_addr_cursor ac; struct afs_vnode *vnode; @@ -779,13 +779,13 @@ struct afs_fs_cursor { short index; /* Current server */ short error; unsigned short flags; -#define AFS_FS_CURSOR_STOP 0x0001 /* Set to cease iteration */ -#define AFS_FS_CURSOR_VBUSY 0x0002 /* Set if seen VBUSY */ -#define AFS_FS_CURSOR_VMOVED 0x0004 /* Set if seen VMOVED */ -#define AFS_FS_CURSOR_VNOVOL 0x0008 /* Set if seen VNOVOL */ -#define AFS_FS_CURSOR_CUR_ONLY 0x0010 /* Set if current server only (file lock held) */ -#define AFS_FS_CURSOR_NO_VSLEEP 0x0020 /* Set to prevent sleep on VBUSY, VOFFLINE, ... */ -#define AFS_FS_CURSOR_INTR 0x0040 /* Set if op is interruptible */ +#define AFS_OPERATION_STOP 0x0001 /* Set to cease iteration */ +#define AFS_OPERATION_VBUSY 0x0002 /* Set if seen VBUSY */ +#define AFS_OPERATION_VMOVED 0x0004 /* Set if seen VMOVED */ +#define AFS_OPERATION_VNOVOL 0x0008 /* Set if seen VNOVOL */ +#define AFS_OPERATION_CUR_ONLY 0x0010 /* Set if current server only (file lock held) */ +#define AFS_OPERATION_NO_VSLEEP 0x0020 /* Set to prevent sleep on VBUSY, VOFFLINE, ... */ +#define AFS_OPERATION_INTR 0x0040 /* Set if op is interruptible */ unsigned short nr_iterations; /* Number of server iterations */ }; @@ -958,35 +958,35 @@ extern int afs_flock(struct file *, int, struct file_lock *); /* * fsclient.c */ -extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_status_cb *, +extern int afs_fs_fetch_file_status(struct afs_operation *, struct afs_status_cb *, struct afs_volsync *); -extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_status_cb *, struct afs_read *); -extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t, +extern int afs_fs_fetch_data(struct afs_operation *, struct afs_status_cb *, struct afs_read *); +extern int afs_fs_create(struct afs_operation *, const char *, umode_t, struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); -extern int afs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char *, bool, +extern int afs_fs_remove(struct afs_operation *, struct afs_vnode *, const char *, bool, struct afs_status_cb *); -extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *, +extern int afs_fs_link(struct afs_operation *, struct afs_vnode *, const char *, struct afs_status_cb *, struct afs_status_cb *); -extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, +extern int afs_fs_symlink(struct afs_operation *, const char *, const char *, struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); -extern int afs_fs_rename(struct afs_fs_cursor *, const char *, +extern int afs_fs_rename(struct afs_operation *, const char *, struct afs_vnode *, const char *, struct afs_status_cb *, struct afs_status_cb *); -extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *, +extern int afs_fs_store_data(struct afs_operation *, struct address_space *, pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *); -extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *, struct afs_status_cb *); -extern int afs_fs_get_volume_status(struct afs_fs_cursor *, struct afs_volume_status *); -extern int afs_fs_set_lock(struct afs_fs_cursor *, afs_lock_type_t, struct afs_status_cb *); -extern int afs_fs_extend_lock(struct afs_fs_cursor *, struct afs_status_cb *); -extern int afs_fs_release_lock(struct afs_fs_cursor *, struct afs_status_cb *); +extern int afs_fs_setattr(struct afs_operation *, struct iattr *, struct afs_status_cb *); +extern int afs_fs_get_volume_status(struct afs_operation *, struct afs_volume_status *); +extern int afs_fs_set_lock(struct afs_operation *, afs_lock_type_t, struct afs_status_cb *); +extern int afs_fs_extend_lock(struct afs_operation *, struct afs_status_cb *); +extern int afs_fs_release_lock(struct afs_operation *, struct afs_status_cb *); extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *, struct afs_addr_cursor *, struct key *); extern bool afs_fs_get_capabilities(struct afs_net *, struct afs_server *, struct afs_addr_cursor *, struct key *); -extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *, +extern int afs_fs_inline_bulk_status(struct afs_operation *, struct afs_net *, struct afs_fid *, struct afs_status_cb *, unsigned int, struct afs_volsync *); -extern int afs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *, +extern int afs_fs_fetch_status(struct afs_operation *, struct afs_net *, struct afs_fid *, struct afs_status_cb *, struct afs_volsync *); @@ -995,8 +995,8 @@ struct afs_acl { u8 data[]; }; -extern struct afs_acl *afs_fs_fetch_acl(struct afs_fs_cursor *, struct afs_status_cb *); -extern int afs_fs_store_acl(struct afs_fs_cursor *, const struct afs_acl *, +extern struct afs_acl *afs_fs_fetch_acl(struct afs_operation *, struct afs_status_cb *); +extern int afs_fs_store_acl(struct afs_operation *, const struct afs_acl *, struct afs_status_cb *); /* @@ -1010,7 +1010,7 @@ extern void afs_fs_probe_dispatcher(struct work_struct *); /* * inode.c */ -extern void afs_vnode_commit_status(struct afs_fs_cursor *, +extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode *, unsigned int, const afs_dataversion_t *, @@ -1109,11 +1109,11 @@ static inline void afs_put_sysnames(struct afs_sysnames *sysnames) {} /* * rotate.c */ -extern bool afs_begin_vnode_operation(struct afs_fs_cursor *, struct afs_vnode *, +extern bool afs_begin_vnode_operation(struct afs_operation *, struct afs_vnode *, struct key *, bool); -extern bool afs_select_fileserver(struct afs_fs_cursor *); -extern bool afs_select_current_fileserver(struct afs_fs_cursor *); -extern int afs_end_vnode_operation(struct afs_fs_cursor *); +extern bool afs_select_fileserver(struct afs_operation *); +extern bool afs_select_current_fileserver(struct afs_operation *); +extern int afs_end_vnode_operation(struct afs_operation *); /* * rxrpc.c @@ -1135,10 +1135,10 @@ extern void afs_send_simple_reply(struct afs_call *, const void *, size_t); extern int afs_extract_data(struct afs_call *, bool); extern int afs_protocol_error(struct afs_call *, enum afs_eproto_cause); -static inline void afs_set_fc_call(struct afs_call *call, struct afs_fs_cursor *fc) +static inline void afs_set_fc_call(struct afs_call *call, struct afs_operation *op) { - call->intr = fc->flags & AFS_FS_CURSOR_INTR; - fc->type = call->type; + call->intr = op->flags & AFS_OPERATION_INTR; + op->type = call->type; } static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size) @@ -1256,7 +1256,7 @@ extern void afs_manage_servers(struct work_struct *); extern void afs_servers_timer(struct timer_list *); extern void afs_fs_probe_timer(struct timer_list *); extern void __net_exit afs_purge_servers(struct afs_net *); -extern bool afs_check_server_record(struct afs_fs_cursor *, struct afs_server *); +extern bool afs_check_server_record(struct afs_operation *, struct afs_server *); static inline void afs_inc_servers_outstanding(struct afs_net *net) { @@ -1358,7 +1358,7 @@ extern struct afs_volume *afs_create_volume(struct afs_fs_context *); extern void afs_activate_volume(struct afs_volume *); extern void afs_deactivate_volume(struct afs_volume *); extern void afs_put_volume(struct afs_cell *, struct afs_volume *); -extern int afs_check_volume_status(struct afs_volume *, struct afs_fs_cursor *); +extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *); /* * write.c @@ -1387,34 +1387,34 @@ extern ssize_t afs_listxattr(struct dentry *, char *, size_t); /* * yfsclient.c */ -extern int yfs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_status_cb *, +extern int yfs_fs_fetch_file_status(struct afs_operation *, struct afs_status_cb *, struct afs_volsync *); -extern int yfs_fs_fetch_data(struct afs_fs_cursor *, struct afs_status_cb *, struct afs_read *); -extern int yfs_fs_create_file(struct afs_fs_cursor *, const char *, umode_t, struct afs_status_cb *, +extern int yfs_fs_fetch_data(struct afs_operation *, struct afs_status_cb *, struct afs_read *); +extern int yfs_fs_create_file(struct afs_operation *, const char *, umode_t, struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); -extern int yfs_fs_make_dir(struct afs_fs_cursor *, const char *, umode_t, struct afs_status_cb *, +extern int yfs_fs_make_dir(struct afs_operation *, const char *, umode_t, struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); -extern int yfs_fs_remove_file2(struct afs_fs_cursor *, struct afs_vnode *, const char *, +extern int yfs_fs_remove_file2(struct afs_operation *, struct afs_vnode *, const char *, struct afs_status_cb *, struct afs_status_cb *); -extern int yfs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char *, bool, +extern int yfs_fs_remove(struct afs_operation *, struct afs_vnode *, const char *, bool, struct afs_status_cb *); -extern int yfs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *, +extern int yfs_fs_link(struct afs_operation *, struct afs_vnode *, const char *, struct afs_status_cb *, struct afs_status_cb *); -extern int yfs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, +extern int yfs_fs_symlink(struct afs_operation *, const char *, const char *, struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); -extern int yfs_fs_rename(struct afs_fs_cursor *, const char *, struct afs_vnode *, const char *, +extern int yfs_fs_rename(struct afs_operation *, const char *, struct afs_vnode *, const char *, struct afs_status_cb *, struct afs_status_cb *); -extern int yfs_fs_store_data(struct afs_fs_cursor *, struct address_space *, +extern int yfs_fs_store_data(struct afs_operation *, struct address_space *, pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *); -extern int yfs_fs_setattr(struct afs_fs_cursor *, struct iattr *, struct afs_status_cb *); -extern int yfs_fs_get_volume_status(struct afs_fs_cursor *, struct afs_volume_status *); -extern int yfs_fs_set_lock(struct afs_fs_cursor *, afs_lock_type_t, struct afs_status_cb *); -extern int yfs_fs_extend_lock(struct afs_fs_cursor *, struct afs_status_cb *); -extern int yfs_fs_release_lock(struct afs_fs_cursor *, struct afs_status_cb *); -extern int yfs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *, +extern int yfs_fs_setattr(struct afs_operation *, struct iattr *, struct afs_status_cb *); +extern int yfs_fs_get_volume_status(struct afs_operation *, struct afs_volume_status *); +extern int yfs_fs_set_lock(struct afs_operation *, afs_lock_type_t, struct afs_status_cb *); +extern int yfs_fs_extend_lock(struct afs_operation *, struct afs_status_cb *); +extern int yfs_fs_release_lock(struct afs_operation *, struct afs_status_cb *); +extern int yfs_fs_fetch_status(struct afs_operation *, struct afs_net *, struct afs_fid *, struct afs_status_cb *, struct afs_volsync *); -extern int yfs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *, +extern int yfs_fs_inline_bulk_status(struct afs_operation *, struct afs_net *, struct afs_fid *, struct afs_status_cb *, unsigned int, struct afs_volsync *); @@ -1429,9 +1429,9 @@ struct yfs_acl { }; extern void yfs_free_opaque_acl(struct yfs_acl *); -extern struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *, struct yfs_acl *, +extern struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_operation *, struct yfs_acl *, struct afs_status_cb *); -extern int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *, const struct afs_acl *, +extern int yfs_fs_store_opaque_acl2(struct afs_operation *, const struct afs_acl *, struct afs_status_cb *); /* @@ -1447,10 +1447,10 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode) return &vnode->vfs_inode; } -static inline void afs_check_for_remote_deletion(struct afs_fs_cursor *fc, +static inline void afs_check_for_remote_deletion(struct afs_operation *op, struct afs_vnode *vnode) { - if (fc->ac.error == -ENOENT) { + if (op->ac.error == -ENOENT) { set_bit(AFS_VNODE_DELETED, &vnode->flags); afs_break_callback(vnode, afs_cb_break_for_deleted); } diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index 46b68da89faa..c930033473f6 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -20,20 +20,20 @@ * Fileserver operations are serialised on the server by vnode, so we serialise * them here also using the io_lock. */ -bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode, +bool afs_begin_vnode_operation(struct afs_operation *op, struct afs_vnode *vnode, struct key *key, bool intr) { - memset(fc, 0, sizeof(*fc)); - fc->vnode = vnode; - fc->key = key; - fc->ac.error = SHRT_MAX; - fc->error = -EDESTADDRREQ; + memset(op, 0, sizeof(*op)); + op->vnode = vnode; + op->key = key; + op->ac.error = SHRT_MAX; + op->error = -EDESTADDRREQ; if (intr) { - fc->flags |= AFS_FS_CURSOR_INTR; + op->flags |= AFS_OPERATION_INTR; if (mutex_lock_interruptible(&vnode->io_lock) < 0) { - fc->error = -EINTR; - fc->flags |= AFS_FS_CURSOR_STOP; + op->error = -EINTR; + op->flags |= AFS_OPERATION_STOP; return false; } } else { @@ -41,7 +41,7 @@ bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode } if (vnode->lock_state != AFS_VNODE_LOCK_NONE) - fc->flags |= AFS_FS_CURSOR_CUR_ONLY; + op->flags |= AFS_OPERATION_CUR_ONLY; return true; } @@ -49,26 +49,26 @@ bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode * Begin iteration through a server list, starting with the vnode's last used * server if possible, or the last recorded good server if not. */ -static bool afs_start_fs_iteration(struct afs_fs_cursor *fc, +static bool afs_start_fs_iteration(struct afs_operation *op, struct afs_vnode *vnode) { struct afs_cb_interest *cbi; int i; read_lock(&vnode->volume->servers_lock); - fc->server_list = afs_get_serverlist(vnode->volume->servers); + op->server_list = afs_get_serverlist(vnode->volume->servers); read_unlock(&vnode->volume->servers_lock); - fc->untried = (1UL << fc->server_list->nr_servers) - 1; - fc->index = READ_ONCE(fc->server_list->preferred); + op->untried = (1UL << op->server_list->nr_servers) - 1; + op->index = READ_ONCE(op->server_list->preferred); 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++) { - if (fc->server_list->servers[i].cb_interest == cbi) { - fc->index = i; + for (i = 0; i < op->server_list->nr_servers; i++) { + if (op->server_list->servers[i].cb_interest == cbi) { + op->index = i; goto found_interest; } } @@ -77,8 +77,8 @@ static bool afs_start_fs_iteration(struct afs_fs_cursor *fc, * serving this vnode, then we can't switch to another server * and have to return an error. */ - if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) { - fc->error = -ESTALE; + if (op->flags & AFS_OPERATION_CUR_ONLY) { + op->error = -ESTALE; return false; } @@ -118,12 +118,12 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code) /* * Sleep and retry the operation to the same fileserver. */ -static bool afs_sleep_and_retry(struct afs_fs_cursor *fc) +static bool afs_sleep_and_retry(struct afs_operation *op) { - if (fc->flags & AFS_FS_CURSOR_INTR) { + if (op->flags & AFS_OPERATION_INTR) { msleep_interruptible(1000); if (signal_pending(current)) { - fc->error = -ERESTARTSYS; + op->error = -ERESTARTSYS; return false; } } else { @@ -137,26 +137,26 @@ static bool afs_sleep_and_retry(struct afs_fs_cursor *fc) * Select the fileserver to use. May be called multiple times to rotate * through the fileservers. */ -bool afs_select_fileserver(struct afs_fs_cursor *fc) +bool afs_select_fileserver(struct afs_operation *op) { struct afs_addr_list *alist; struct afs_server *server; - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode *vnode = op->vnode; struct afs_error e; u32 rtt; - int error = fc->ac.error, i; + int error = op->ac.error, i; _enter("%lx[%d],%lx[%d],%d,%d", - fc->untried, fc->index, - fc->ac.tried, fc->ac.index, - error, fc->ac.abort_code); + op->untried, op->index, + op->ac.tried, op->ac.index, + error, op->ac.abort_code); - if (fc->flags & AFS_FS_CURSOR_STOP) { + if (op->flags & AFS_OPERATION_STOP) { _leave(" = f [stopped]"); return false; } - fc->nr_iterations++; + op->nr_iterations++; /* Evaluate the result of the previous operation, if there was one. */ switch (error) { @@ -166,8 +166,8 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) case 0: default: /* Success or local failure. Stop. */ - fc->error = error; - fc->flags |= AFS_FS_CURSOR_STOP; + op->error = error; + op->flags |= AFS_OPERATION_STOP; _leave(" = f [okay/local %d]", error); return false; @@ -175,42 +175,42 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) /* The far side rejected the operation on some grounds. This * might involve the server being busy or the volume having been moved. */ - switch (fc->ac.abort_code) { + switch (op->ac.abort_code) { case VNOVOL: /* This fileserver doesn't know about the volume. * - May indicate that the VL is wrong - retry once and compare * the results. * - May indicate that the fileserver couldn't attach to the vol. */ - if (fc->flags & AFS_FS_CURSOR_VNOVOL) { - fc->error = -EREMOTEIO; + if (op->flags & AFS_OPERATION_VNOVOL) { + op->error = -EREMOTEIO; goto next_server; } write_lock(&vnode->volume->servers_lock); - fc->server_list->vnovol_mask |= 1 << fc->index; + op->server_list->vnovol_mask |= 1 << op->index; write_unlock(&vnode->volume->servers_lock); set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags); - error = afs_check_volume_status(vnode->volume, fc); + error = afs_check_volume_status(vnode->volume, op); if (error < 0) goto failed_set_error; if (test_bit(AFS_VOLUME_DELETED, &vnode->volume->flags)) { - fc->error = -ENOMEDIUM; + op->error = -ENOMEDIUM; goto failed; } /* If the server list didn't change, then assume that * it's the fileserver having trouble. */ - if (vnode->volume->servers == fc->server_list) { - fc->error = -EREMOTEIO; + if (vnode->volume->servers == op->server_list) { + op->error = -EREMOTEIO; goto next_server; } /* Try again */ - fc->flags |= AFS_FS_CURSOR_VNOVOL; + op->flags |= AFS_OPERATION_VNOVOL; _leave(" = t [vnovol]"); return true; @@ -220,20 +220,20 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) case VONLINE: case VDISKFULL: case VOVERQUOTA: - fc->error = afs_abort_to_error(fc->ac.abort_code); + op->error = afs_abort_to_error(op->ac.abort_code); goto next_server; case VOFFLINE: if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags)) { - afs_busy(vnode->volume, fc->ac.abort_code); + afs_busy(vnode->volume, op->ac.abort_code); clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags); } - if (fc->flags & AFS_FS_CURSOR_NO_VSLEEP) { - fc->error = -EADV; + if (op->flags & AFS_OPERATION_NO_VSLEEP) { + op->error = -EADV; goto failed; } - if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) { - fc->error = -ESTALE; + if (op->flags & AFS_OPERATION_CUR_ONLY) { + op->error = -ESTALE; goto failed; } goto busy; @@ -244,17 +244,17 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) /* Retry after going round all the servers unless we * have a file lock we need to maintain. */ - if (fc->flags & AFS_FS_CURSOR_NO_VSLEEP) { - fc->error = -EBUSY; + if (op->flags & AFS_OPERATION_NO_VSLEEP) { + op->error = -EBUSY; goto failed; } if (!test_and_set_bit(AFS_VOLUME_BUSY, &vnode->volume->flags)) { - afs_busy(vnode->volume, fc->ac.abort_code); + afs_busy(vnode->volume, op->ac.abort_code); clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags); } busy: - if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) { - if (!afs_sleep_and_retry(fc)) + if (op->flags & AFS_OPERATION_CUR_ONLY) { + if (!afs_sleep_and_retry(op)) goto failed; /* Retry with same server & address */ @@ -262,7 +262,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) return true; } - fc->flags |= AFS_FS_CURSOR_VBUSY; + op->flags |= AFS_OPERATION_VBUSY; goto next_server; case VMOVED: @@ -273,15 +273,15 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) * We also limit the number of VMOVED hops we will * honour, just in case someone sets up a loop. */ - if (fc->flags & AFS_FS_CURSOR_VMOVED) { - fc->error = -EREMOTEIO; + if (op->flags & AFS_OPERATION_VMOVED) { + op->error = -EREMOTEIO; goto failed; } - fc->flags |= AFS_FS_CURSOR_VMOVED; + op->flags |= AFS_OPERATION_VMOVED; set_bit(AFS_VOLUME_WAIT, &vnode->volume->flags); set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags); - error = afs_check_volume_status(vnode->volume, fc); + error = afs_check_volume_status(vnode->volume, op); if (error < 0) goto failed_set_error; @@ -294,8 +294,8 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) * * TODO: Retry a few times with sleeps. */ - if (vnode->volume->servers == fc->server_list) { - fc->error = -ENOMEDIUM; + if (vnode->volume->servers == op->server_list) { + op->error = -ENOMEDIUM; goto failed; } @@ -304,13 +304,13 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) default: clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags); clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags); - fc->error = afs_abort_to_error(fc->ac.abort_code); + op->error = afs_abort_to_error(op->ac.abort_code); goto failed; } case -ETIMEDOUT: case -ETIME: - if (fc->error != -EDESTADDRREQ) + if (op->error != -EDESTADDRREQ) goto iterate_address; /* Fall through */ case -ERFKILL: @@ -320,83 +320,83 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) case -EHOSTDOWN: case -ECONNREFUSED: _debug("no conn"); - fc->error = error; + op->error = error; goto iterate_address; case -ECONNRESET: _debug("call reset"); - fc->error = error; + op->error = error; goto failed; } restart_from_beginning: _debug("restart"); - afs_end_cursor(&fc->ac); - afs_put_cb_interest(afs_v2net(vnode), fc->cbi); - fc->cbi = NULL; - afs_put_serverlist(afs_v2net(vnode), fc->server_list); - fc->server_list = NULL; + afs_end_cursor(&op->ac); + afs_put_cb_interest(afs_v2net(vnode), op->cbi); + op->cbi = NULL; + afs_put_serverlist(afs_v2net(vnode), op->server_list); + op->server_list = NULL; start: _debug("start"); /* See if we need to do an update of the volume record. Note that the * volume may have moved or even have been deleted. */ - error = afs_check_volume_status(vnode->volume, fc); + error = afs_check_volume_status(vnode->volume, op); if (error < 0) goto failed_set_error; - if (!afs_start_fs_iteration(fc, vnode)) + if (!afs_start_fs_iteration(op, vnode)) goto failed; _debug("__ VOL %llx __", vnode->volume->vid); pick_server: - _debug("pick [%lx]", fc->untried); + _debug("pick [%lx]", op->untried); - error = afs_wait_for_fs_probes(fc->server_list, fc->untried); + error = afs_wait_for_fs_probes(op->server_list, op->untried); if (error < 0) goto failed_set_error; /* Pick the untried server with the lowest RTT. If we have outstanding * callbacks, we stick with the server we're already using if we can. */ - if (fc->cbi) { - _debug("cbi %u", fc->index); - if (test_bit(fc->index, &fc->untried)) + if (op->cbi) { + _debug("cbi %u", op->index); + if (test_bit(op->index, &op->untried)) goto selected_server; - afs_put_cb_interest(afs_v2net(vnode), fc->cbi); - fc->cbi = NULL; + afs_put_cb_interest(afs_v2net(vnode), op->cbi); + op->cbi = NULL; _debug("nocbi"); } - fc->index = -1; + op->index = -1; rtt = U32_MAX; - for (i = 0; i < fc->server_list->nr_servers; i++) { - struct afs_server *s = fc->server_list->servers[i].server; + for (i = 0; i < op->server_list->nr_servers; i++) { + struct afs_server *s = op->server_list->servers[i].server; - if (!test_bit(i, &fc->untried) || !s->probe.responded) + if (!test_bit(i, &op->untried) || !s->probe.responded) continue; if (s->probe.rtt < rtt) { - fc->index = i; + op->index = i; rtt = s->probe.rtt; } } - if (fc->index == -1) + if (op->index == -1) goto no_more_servers; selected_server: - _debug("use %d", fc->index); - __clear_bit(fc->index, &fc->untried); + _debug("use %d", op->index); + __clear_bit(op->index, &op->untried); /* We're starting on a different fileserver from the list. We need to * check it, create a callback intercept, find its address list and * probe its capabilities before we use it. */ - ASSERTCMP(fc->ac.alist, ==, NULL); - server = fc->server_list->servers[fc->index].server; + ASSERTCMP(op->ac.alist, ==, NULL); + server = op->server_list->servers[op->index].server; - if (!afs_check_server_record(fc, server)) + if (!afs_check_server_record(op, server)) goto failed; _debug("USING SERVER: %pU", &server->uuid); @@ -406,12 +406,12 @@ selected_server: * break request before we've finished decoding the reply and * installing the vnode. */ - error = afs_register_server_cb_interest(vnode, fc->server_list, - fc->index); + error = afs_register_server_cb_interest(vnode, op->server_list, + op->index); if (error < 0) goto failed_set_error; - fc->cbi = afs_get_cb_interest( + op->cbi = afs_get_cb_interest( rcu_dereference_protected(vnode->cb_interest, lockdep_is_held(&vnode->io_lock))); @@ -421,44 +421,44 @@ selected_server: afs_get_addrlist(alist); read_unlock(&server->fs_lock); - memset(&fc->ac, 0, sizeof(fc->ac)); + memset(&op->ac, 0, sizeof(op->ac)); - if (!fc->ac.alist) - fc->ac.alist = alist; + if (!op->ac.alist) + op->ac.alist = alist; else afs_put_addrlist(alist); - fc->ac.index = -1; + op->ac.index = -1; iterate_address: - ASSERT(fc->ac.alist); + ASSERT(op->ac.alist); /* Iterate over the current server's address list to try and find an * address on which it will respond to us. */ - if (!afs_iterate_addresses(&fc->ac)) + if (!afs_iterate_addresses(&op->ac)) goto next_server; - _debug("address [%u] %u/%u", fc->index, fc->ac.index, fc->ac.alist->nr_addrs); + _debug("address [%u] %u/%u", op->index, op->ac.index, op->ac.alist->nr_addrs); _leave(" = t"); return true; next_server: _debug("next"); - afs_end_cursor(&fc->ac); + afs_end_cursor(&op->ac); goto pick_server; no_more_servers: /* That's all the servers poked to no good effect. Try again if some * of them were busy. */ - if (fc->flags & AFS_FS_CURSOR_VBUSY) + if (op->flags & AFS_OPERATION_VBUSY) goto restart_from_beginning; e.error = -EDESTADDRREQ; e.responded = false; - for (i = 0; i < fc->server_list->nr_servers; i++) { - struct afs_server *s = fc->server_list->servers[i].server; + for (i = 0; i < op->server_list->nr_servers; i++) { + struct afs_server *s = op->server_list->servers[i].server; afs_prioritise_error(&e, READ_ONCE(s->probe.error), s->probe.abort_code); @@ -467,11 +467,11 @@ no_more_servers: error = e.error; failed_set_error: - fc->error = error; + op->error = error; failed: - fc->flags |= AFS_FS_CURSOR_STOP; - afs_end_cursor(&fc->ac); - _leave(" = f [failed %d]", fc->error); + op->flags |= AFS_OPERATION_STOP; + afs_end_cursor(&op->ac); + _leave(" = f [failed %d]", op->error); return false; } @@ -480,12 +480,12 @@ failed: * fileserver. We use this when we have a lock on that file, which is backed * only by the fileserver we obtained it from. */ -bool afs_select_current_fileserver(struct afs_fs_cursor *fc) +bool afs_select_current_fileserver(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode *vnode = op->vnode; struct afs_cb_interest *cbi; struct afs_addr_list *alist; - int error = fc->ac.error; + int error = op->ac.error; _enter(""); @@ -495,12 +495,12 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc) switch (error) { case SHRT_MAX: if (!cbi) { - fc->error = -ESTALE; - fc->flags |= AFS_FS_CURSOR_STOP; + op->error = -ESTALE; + op->flags |= AFS_OPERATION_STOP; return false; } - fc->cbi = afs_get_cb_interest(cbi); + op->cbi = afs_get_cb_interest(cbi); read_lock(&cbi->server->fs_lock); alist = rcu_dereference_protected(cbi->server->addresses, @@ -508,27 +508,27 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc) afs_get_addrlist(alist); read_unlock(&cbi->server->fs_lock); if (!alist) { - fc->error = -ESTALE; - fc->flags |= AFS_FS_CURSOR_STOP; + op->error = -ESTALE; + op->flags |= AFS_OPERATION_STOP; return false; } - memset(&fc->ac, 0, sizeof(fc->ac)); - fc->ac.alist = alist; - fc->ac.index = -1; + memset(&op->ac, 0, sizeof(op->ac)); + op->ac.alist = alist; + op->ac.index = -1; goto iterate_address; case 0: default: /* Success or local failure. Stop. */ - fc->error = error; - fc->flags |= AFS_FS_CURSOR_STOP; + op->error = error; + op->flags |= AFS_OPERATION_STOP; _leave(" = f [okay/local %d]", error); return false; case -ECONNABORTED: - fc->error = afs_abort_to_error(fc->ac.abort_code); - fc->flags |= AFS_FS_CURSOR_STOP; + op->error = afs_abort_to_error(op->ac.abort_code); + op->flags |= AFS_OPERATION_STOP; _leave(" = f [abort]"); return false; @@ -541,7 +541,7 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc) case -ETIMEDOUT: case -ETIME: _debug("no conn"); - fc->error = error; + op->error = error; goto iterate_address; } @@ -549,19 +549,19 @@ iterate_address: /* Iterate over the current server's address list to try and find an * address on which it will respond to us. */ - if (afs_iterate_addresses(&fc->ac)) { + if (afs_iterate_addresses(&op->ac)) { _leave(" = t"); return true; } - afs_end_cursor(&fc->ac); + afs_end_cursor(&op->ac); return false; } /* * Dump cursor state in the case of the error being EDESTADDRREQ. */ -static void afs_dump_edestaddrreq(const struct afs_fs_cursor *fc) +static void afs_dump_edestaddrreq(const struct afs_operation *op) { static int count; int i; @@ -574,12 +574,12 @@ static void afs_dump_edestaddrreq(const struct afs_fs_cursor *fc) pr_notice("EDESTADDR occurred\n"); pr_notice("FC: cbb=%x cbb2=%x fl=%hx err=%hd\n", - fc->cb_break, fc->cb_break_2, fc->flags, fc->error); + op->cb_break, op->cb_break_2, op->flags, op->error); pr_notice("FC: ut=%lx ix=%d ni=%u\n", - fc->untried, fc->index, fc->nr_iterations); + op->untried, op->index, op->nr_iterations); - if (fc->server_list) { - const struct afs_server_list *sl = fc->server_list; + if (op->server_list) { + const struct afs_server_list *sl = op->server_list; pr_notice("FC: SL nr=%u pr=%u vnov=%hx\n", sl->nr_servers, sl->preferred, sl->vnovol_mask); for (i = 0; i < sl->nr_servers; i++) { @@ -595,39 +595,39 @@ static void afs_dump_edestaddrreq(const struct afs_fs_cursor *fc) a->preferred); pr_notice("FC: - R=%lx F=%lx\n", a->responded, a->failed); - if (a == fc->ac.alist) + if (a == op->ac.alist) pr_notice("FC: - current\n"); } } } pr_notice("AC: t=%lx ax=%u ac=%d er=%d r=%u ni=%u\n", - fc->ac.tried, fc->ac.index, fc->ac.abort_code, fc->ac.error, - fc->ac.responded, fc->ac.nr_iterations); + op->ac.tried, op->ac.index, op->ac.abort_code, op->ac.error, + op->ac.responded, op->ac.nr_iterations); rcu_read_unlock(); } /* * Tidy up a filesystem cursor and unlock the vnode. */ -int afs_end_vnode_operation(struct afs_fs_cursor *fc) +int afs_end_vnode_operation(struct afs_operation *op) { - struct afs_net *net = afs_v2net(fc->vnode); + struct afs_net *net = afs_v2net(op->vnode); - if (fc->error == -EDESTADDRREQ || - fc->error == -EADDRNOTAVAIL || - fc->error == -ENETUNREACH || - fc->error == -EHOSTUNREACH) - afs_dump_edestaddrreq(fc); + if (op->error == -EDESTADDRREQ || + op->error == -EADDRNOTAVAIL || + op->error == -ENETUNREACH || + op->error == -EHOSTUNREACH) + afs_dump_edestaddrreq(op); - mutex_unlock(&fc->vnode->io_lock); + mutex_unlock(&op->vnode->io_lock); - afs_end_cursor(&fc->ac); - afs_put_cb_interest(net, fc->cbi); - afs_put_serverlist(net, fc->server_list); + afs_end_cursor(&op->ac); + afs_put_cb_interest(net, op->cbi); + afs_put_serverlist(net, op->server_list); - if (fc->error == -ECONNABORTED) - fc->error = afs_abort_to_error(fc->ac.abort_code); + if (op->error == -ECONNABORTED) + op->error = afs_abort_to_error(op->ac.abort_code); - return fc->error; + return op->error; } diff --git a/fs/afs/server.c b/fs/afs/server.c index 5ed90f419c54..3008f2ecfeee 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -571,7 +571,7 @@ void afs_purge_servers(struct afs_net *net) /* * Get an update for a server's address list. */ -static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct afs_server *server) +static noinline bool afs_update_server_record(struct afs_operation *fc, struct afs_server *server) { struct afs_addr_list *alist, *discard; @@ -585,7 +585,7 @@ static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct a if (IS_ERR(alist)) { if ((PTR_ERR(alist) == -ERESTARTSYS || PTR_ERR(alist) == -EINTR) && - !(fc->flags & AFS_FS_CURSOR_INTR) && + !(fc->flags & AFS_OPERATION_INTR) && server->addresses) { _leave(" = t [intr]"); return true; @@ -613,7 +613,7 @@ static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct a /* * See if a server's address list needs updating. */ -bool afs_check_server_record(struct afs_fs_cursor *fc, struct afs_server *server) +bool afs_check_server_record(struct afs_operation *fc, struct afs_server *server) { bool success; int ret, retries = 0; @@ -642,7 +642,7 @@ update: wait: ret = wait_on_bit(&server->flags, AFS_SERVER_FL_UPDATING, - (fc->flags & AFS_FS_CURSOR_INTR) ? + (fc->flags & AFS_OPERATION_INTR) ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); if (ret == -ERESTARTSYS) { fc->error = ret; diff --git a/fs/afs/super.c b/fs/afs/super.c index dda7a9a66848..9f412d7e7edf 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -715,7 +715,7 @@ static void afs_destroy_inode(struct inode *inode) static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct afs_super_info *as = AFS_FS_S(dentry->d_sb); - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_volume_status vs; struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); struct key *key; @@ -738,7 +738,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) ret = -ERESTARTSYS; if (afs_begin_vnode_operation(&fc, vnode, key, true)) { - fc.flags |= AFS_FS_CURSOR_NO_VSLEEP; + fc.flags |= AFS_OPERATION_NO_VSLEEP; while (afs_select_fileserver(&fc)) { fc.cb_break = afs_calc_vnode_cb_break(vnode); afs_fs_get_volume_status(&fc, &vs); diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 249000195f8a..96351088a578 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -280,7 +280,7 @@ error: /* * Make sure the volume record is up to date. */ -int afs_check_volume_status(struct afs_volume *volume, struct afs_fs_cursor *fc) +int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *fc) { int ret, retries = 0; @@ -315,7 +315,7 @@ wait: } ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT, - (fc->flags & AFS_FS_CURSOR_INTR) ? + (fc->flags & AFS_OPERATION_INTR) ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); if (ret == -ERESTARTSYS) { _leave(" = %d", ret); diff --git a/fs/afs/write.c b/fs/afs/write.c index cb76566763db..1a8af44ea36b 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -356,7 +356,7 @@ static int afs_store_data(struct address_space *mapping, unsigned offset, unsigned to) { struct afs_vnode *vnode = AFS_FS_I(mapping->host); - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_wb_key *wbk = NULL; struct list_head *p; diff --git a/fs/afs/xattr.c b/fs/afs/xattr.c index 7af41fd5f3ee..bf645f1c90b0 100644 --- a/fs/afs/xattr.c +++ b/fs/afs/xattr.c @@ -42,7 +42,7 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler, struct inode *inode, const char *name, void *buffer, size_t size) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_acl *acl = NULL; @@ -100,7 +100,7 @@ static int afs_xattr_set_acl(const struct xattr_handler *handler, struct inode *inode, const char *name, const void *buffer, size_t size, int flags) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_acl *acl = NULL; @@ -165,7 +165,7 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler, struct inode *inode, const char *name, void *buffer, size_t size) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *vnode = AFS_FS_I(inode); struct yfs_acl *yacl = NULL; @@ -270,7 +270,7 @@ static int afs_xattr_set_yfs(const struct xattr_handler *handler, struct inode *inode, const char *name, const void *buffer, size_t size, int flags) { - struct afs_fs_cursor fc; + struct afs_operation fc; struct afs_status_cb *scb; struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_acl *acl = NULL; diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index bf74c679c02b..360b4a560ba7 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -387,7 +387,7 @@ static const struct afs_call_type yfs_RXYFSFetchStatus_vnode = { /* * Fetch the status information for a file. */ -int yfs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_status_cb *scb, +int yfs_fs_fetch_file_status(struct afs_operation *fc, struct afs_status_cb *scb, struct afs_volsync *volsync) { struct afs_vnode *vnode = fc->vnode; @@ -575,7 +575,7 @@ static const struct afs_call_type yfs_RXYFSFetchData64 = { /* * Fetch data from a file. */ -int yfs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_status_cb *scb, +int yfs_fs_fetch_data(struct afs_operation *fc, struct afs_status_cb *scb, struct afs_read *req) { struct afs_vnode *vnode = fc->vnode; @@ -657,7 +657,7 @@ static const struct afs_call_type afs_RXFSCreateFile = { /* * Create a file. */ -int yfs_fs_create_file(struct afs_fs_cursor *fc, +int yfs_fs_create_file(struct afs_operation *fc, const char *name, umode_t mode, struct afs_status_cb *dvnode_scb, @@ -721,7 +721,7 @@ static const struct afs_call_type yfs_RXFSMakeDir = { /* * Make a directory. */ -int yfs_fs_make_dir(struct afs_fs_cursor *fc, +int yfs_fs_make_dir(struct afs_operation *fc, const char *name, umode_t mode, struct afs_status_cb *dvnode_scb, @@ -811,7 +811,7 @@ static const struct afs_call_type yfs_RXYFSRemoveFile2 = { /* * Remove a file and retrieve new file status. */ -int yfs_fs_remove_file2(struct afs_fs_cursor *fc, struct afs_vnode *vnode, +int yfs_fs_remove_file2(struct afs_operation *fc, struct afs_vnode *vnode, const char *name, struct afs_status_cb *dvnode_scb, struct afs_status_cb *vnode_scb) { @@ -896,7 +896,7 @@ static const struct afs_call_type yfs_RXYFSRemoveDir = { /* * remove a file or directory */ -int yfs_fs_remove(struct afs_fs_cursor *fc, struct afs_vnode *vnode, +int yfs_fs_remove(struct afs_operation *fc, struct afs_vnode *vnode, const char *name, bool isdir, struct afs_status_cb *dvnode_scb) { @@ -973,7 +973,7 @@ static const struct afs_call_type yfs_RXYFSLink = { /* * Make a hard link. */ -int yfs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, +int yfs_fs_link(struct afs_operation *fc, struct afs_vnode *vnode, const char *name, struct afs_status_cb *dvnode_scb, struct afs_status_cb *vnode_scb) @@ -1057,7 +1057,7 @@ static const struct afs_call_type yfs_RXYFSSymlink = { /* * Create a symbolic link. */ -int yfs_fs_symlink(struct afs_fs_cursor *fc, +int yfs_fs_symlink(struct afs_operation *fc, const char *name, const char *contents, struct afs_status_cb *dvnode_scb, @@ -1148,7 +1148,7 @@ static const struct afs_call_type yfs_RXYFSRename = { /* * Rename a file or directory. */ -int yfs_fs_rename(struct afs_fs_cursor *fc, +int yfs_fs_rename(struct afs_operation *fc, const char *orig_name, struct afs_vnode *new_dvnode, const char *new_name, @@ -1212,7 +1212,7 @@ static const struct afs_call_type yfs_RXYFSStoreData64 = { /* * Store a set of pages to a large file. */ -int yfs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping, +int yfs_fs_store_data(struct afs_operation *fc, struct address_space *mapping, pgoff_t first, pgoff_t last, unsigned offset, unsigned to, struct afs_status_cb *scb) @@ -1299,7 +1299,7 @@ static const struct afs_call_type yfs_RXYFSStoreData64_as_Status = { * Set the attributes on a file, using YFS.StoreData64 rather than * YFS.StoreStatus so as to alter the file size also. */ -static int yfs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr, +static int yfs_fs_setattr_size(struct afs_operation *fc, struct iattr *attr, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; @@ -1345,7 +1345,7 @@ static int yfs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr, * Set the attributes on a file, using YFS.StoreData64 if there's a change in * file size, and YFS.StoreStatus otherwise. */ -int yfs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr, +int yfs_fs_setattr(struct afs_operation *fc, struct iattr *attr, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; @@ -1526,7 +1526,7 @@ static const struct afs_call_type yfs_RXYFSGetVolumeStatus = { /* * fetch the status of a volume */ -int yfs_fs_get_volume_status(struct afs_fs_cursor *fc, +int yfs_fs_get_volume_status(struct afs_operation *fc, struct afs_volume_status *vs) { struct afs_vnode *vnode = fc->vnode; @@ -1598,7 +1598,7 @@ static const struct afs_call_type yfs_RXYFSReleaseLock = { /* * Set a lock on a file */ -int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type, +int yfs_fs_set_lock(struct afs_operation *fc, afs_lock_type_t type, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; @@ -1639,7 +1639,7 @@ int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type, /* * extend a lock on a file */ -int yfs_fs_extend_lock(struct afs_fs_cursor *fc, struct afs_status_cb *scb) +int yfs_fs_extend_lock(struct afs_operation *fc, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; struct afs_call *call; @@ -1677,7 +1677,7 @@ int yfs_fs_extend_lock(struct afs_fs_cursor *fc, struct afs_status_cb *scb) /* * release a lock on a file */ -int yfs_fs_release_lock(struct afs_fs_cursor *fc, struct afs_status_cb *scb) +int yfs_fs_release_lock(struct afs_operation *fc, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; struct afs_call *call; @@ -1725,7 +1725,7 @@ static const struct afs_call_type yfs_RXYFSFetchStatus = { /* * Fetch the status information for a fid without needing a vnode handle. */ -int yfs_fs_fetch_status(struct afs_fs_cursor *fc, +int yfs_fs_fetch_status(struct afs_operation *fc, struct afs_net *net, struct afs_fid *fid, struct afs_status_cb *scb, @@ -1888,7 +1888,7 @@ static const struct afs_call_type yfs_RXYFSInlineBulkStatus = { /* * Fetch the status information for up to 1024 files */ -int yfs_fs_inline_bulk_status(struct afs_fs_cursor *fc, +int yfs_fs_inline_bulk_status(struct afs_operation *fc, struct afs_net *net, struct afs_fid *fids, struct afs_status_cb *statuses, @@ -2065,7 +2065,7 @@ static const struct afs_call_type yfs_RXYFSFetchOpaqueACL = { /* * Fetch the YFS advanced ACLs for a file. */ -struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *fc, +struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_operation *fc, struct yfs_acl *yacl, struct afs_status_cb *scb) { @@ -2119,7 +2119,7 @@ static const struct afs_call_type yfs_RXYFSStoreOpaqueACL2 = { /* * Fetch the YFS ACL for a file. */ -int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *fc, const struct afs_acl *acl, +int yfs_fs_store_opaque_acl2(struct afs_operation *fc, const struct afs_acl *acl, struct afs_status_cb *scb) { struct afs_vnode *vnode = fc->vnode; -- cgit v1.2.3-70-g09d2 From e49c7b2f6de7ff81ca34c56e4eeb4fa740c099f2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 10 Apr 2020 20:51:51 +0100 Subject: afs: Build an abstraction around an "operation" concept Turn the afs_operation struct into the main way that most fileserver operations are managed. Various things are added to the struct, including the following: (1) All the parameters and results of the relevant operations are moved into it, removing corresponding fields from the afs_call struct. afs_call gets a pointer to the op. (2) The target volume is made the main focus of the operation, rather than the target vnode(s), and a bunch of op->vnode->volume are made op->volume instead. (3) Two vnode records are defined (op->file[]) for the vnode(s) involved in most operations. The vnode record (struct afs_vnode_param) contains: - The vnode pointer. - The fid of the vnode to be included in the parameters or that was returned in the reply (eg. FS.MakeDir). - The status and callback information that may be returned in the reply about the vnode. - Callback break and data version tracking for detecting simultaneous third-parth changes. (4) Pointers to dentries to be updated with new inodes. (5) An operations table pointer. The table includes pointers to functions for issuing AFS and YFS-variant RPCs, handling the success and abort of an operation and handling post-I/O-lock local editing of a directory. To make this work, the following function restructuring is made: (A) The rotation loop that issues calls to fileservers that can be found in each function that wants to issue an RPC (such as afs_mkdir()) is extracted out into common code, in a new file called fs_operation.c. (B) The rotation loops, such as the one in afs_mkdir(), are replaced with a much smaller piece of code that allocates an operation, sets the parameters and then calls out to the common code to do the actual work. (C) The code for handling the success and failure of an operation are moved into operation functions (as (5) above) and these are called from the core code at appropriate times. (D) The pseudo inode getting stuff used by the dynamic root code is moved over into dynroot.c. (E) struct afs_iget_data is absorbed into the operation struct and afs_iget() expects to be given an op pointer and a vnode record. (F) Point (E) doesn't work for the root dir of a volume, but we know the FID in advance (it's always vnode 1, unique 1), so a separate inode getter, afs_root_iget(), is provided to special-case that. (G) The inode status init/update functions now also take an op and a vnode record. (H) The RPC marshalling functions now, for the most part, just take an afs_operation struct as their only argument. All the data they need is held there. The result delivery functions write their answers there as well. (I) The call is attached to the operation and then the operation core does the waiting. And then the new operation code is, for the moment, made to just initialise the operation, get the appropriate vnode I/O locks and do the same rotation loop as before. This lays the foundation for the following changes in the future: (*) Overhauling the rotation (again). (*) Support for asynchronous I/O, where the fileserver rotation must be done asynchronously also. Signed-off-by: David Howells --- fs/afs/Makefile | 1 + fs/afs/afs.h | 1 - fs/afs/callback.c | 7 +- fs/afs/dir.c | 1211 ++++++++++++++++++++------------------------ fs/afs/dir_silly.c | 190 +++---- fs/afs/dynroot.c | 93 ++++ fs/afs/file.c | 62 +-- fs/afs/flock.c | 114 ++--- fs/afs/fs_operation.c | 246 +++++++++ fs/afs/fsclient.c | 1209 ++++++++++++++++++------------------------- fs/afs/inode.c | 440 ++++++++-------- fs/afs/internal.h | 313 +++++++----- fs/afs/rotate.c | 128 ++--- fs/afs/rxrpc.c | 28 +- fs/afs/server.c | 37 +- fs/afs/super.c | 77 ++- fs/afs/volume.c | 12 +- fs/afs/write.c | 148 +++--- fs/afs/xattr.c | 300 +++++------ fs/afs/yfsclient.c | 844 +++++++++++++----------------- include/trace/events/afs.h | 19 +- 21 files changed, 2648 insertions(+), 2832 deletions(-) create mode 100644 fs/afs/fs_operation.c (limited to 'fs/afs') diff --git a/fs/afs/Makefile b/fs/afs/Makefile index 10359bea7070..924f02e9d7e7 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile @@ -18,6 +18,7 @@ kafs-y := \ file.o \ flock.o \ fsclient.o \ + fs_operation.o \ fs_probe.o \ inode.o \ main.o \ diff --git a/fs/afs/afs.h b/fs/afs/afs.h index b6d49d646ade..f8e34406243e 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h @@ -146,7 +146,6 @@ struct afs_file_status { 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 */ diff --git a/fs/afs/callback.c b/fs/afs/callback.c index b16781e1683e..282dbac84435 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -46,7 +46,6 @@ static struct afs_cb_interest *afs_create_interest(struct afs_server *server, refcount_set(&new->usage, 1); new->sb = vnode->vfs_inode.i_sb; - new->vid = vnode->volume->vid; new->server = afs_get_server(server, afs_server_trace_get_new_cbi); INIT_HLIST_NODE(&new->cb_vlink); @@ -286,7 +285,6 @@ static void afs_break_one_callback(struct afs_server *server, struct afs_vol_interest *vi) { struct afs_cb_interest *cbi; - struct afs_iget_data data; struct afs_vnode *vnode; struct inode *inode; @@ -305,15 +303,12 @@ static void afs_break_one_callback(struct afs_server *server, afs_cb_break_for_volume_callback, false); write_unlock(&volume->cb_v_break_lock); } else { - data.volume = NULL; - data.fid = *fid; - /* See if we can find a matching inode - even an I_NEW * inode needs to be marked as it can have its callback * broken before we finish setting up the local inode. */ inode = find_inode_rcu(cbi->sb, fid->vnode, - afs_iget5_test, &data); + afs_ilookup5_test_by_fid, fid); if (inode) { vnode = AFS_FS_I(inode); afs_break_callback(vnode, afs_cb_break_for_callback); diff --git a/fs/afs/dir.c b/fs/afs/dir.c index ff421db40cf2..0d3cf3af0352 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -99,8 +99,6 @@ struct afs_lookup_cookie { bool found; bool one_only; unsigned short nr_fids; - struct inode **inodes; - struct afs_status_cb *statuses; struct afs_fid fids[50]; }; @@ -618,8 +616,8 @@ static int afs_lookup_filldir(struct dir_context *ctx, const char *name, } } else if (cookie->name.len == nlen && memcmp(cookie->name.name, name, nlen) == 0) { - cookie->fids[0].vnode = ino; - cookie->fids[0].unique = dtype; + cookie->fids[1].vnode = ino; + cookie->fids[1].unique = dtype; cookie->found = 1; if (cookie->one_only) return -1; @@ -630,6 +628,80 @@ static int afs_lookup_filldir(struct dir_context *ctx, const char *name, return ret; } +/* + * Deal with the result of a successful lookup operation. Turn all the files + * into inodes and save the first one - which is the one we actually want. + */ +static void afs_do_lookup_success(struct afs_operation *op) +{ + struct afs_vnode_param *vp; + struct afs_vnode *vnode; + struct inode *inode; + u32 abort_code; + int i; + + _enter(""); + + for (i = 0; i < op->nr_files; i++) { + switch (i) { + case 0: + vp = &op->file[0]; + abort_code = vp->scb.status.abort_code; + if (abort_code != 0) { + op->abort_code = abort_code; + op->error = afs_abort_to_error(abort_code); + } + break; + + case 1: + vp = &op->file[1]; + break; + + default: + vp = &op->more_files[i - 2]; + break; + } + + if (!vp->scb.have_status && !vp->scb.have_error) + continue; + + _debug("do [%u]", i); + if (vp->vnode) { + if (!test_bit(AFS_VNODE_UNSET, &vp->vnode->flags)) + afs_vnode_commit_status(op, vp); + } else if (vp->scb.status.abort_code == 0) { + inode = afs_iget(op, vp); + if (!IS_ERR(inode)) { + vnode = AFS_FS_I(inode); + afs_cache_permit(vnode, op->key, + 0 /* Assume vnode->cb_break is 0 */ + + op->cb_v_break, + &vp->scb); + vp->vnode = vnode; + vp->put_vnode = true; + } + } else { + _debug("- abort %d %llx:%llx.%x", + vp->scb.status.abort_code, + vp->fid.vid, vp->fid.vnode, vp->fid.unique); + } + } + + _leave(""); +} + +static const struct afs_operation_ops afs_inline_bulk_status_operation = { + .issue_afs_rpc = afs_fs_inline_bulk_status, + .issue_yfs_rpc = yfs_fs_inline_bulk_status, + .success = afs_do_lookup_success, +}; + +static const struct afs_operation_ops afs_fetch_status_operation = { + .issue_afs_rpc = afs_fs_fetch_status, + .issue_yfs_rpc = yfs_fs_fetch_status, + .success = afs_do_lookup_success, +}; + /* * Do a lookup in a directory. We make use of bulk lookup to query a slew of * files in one go and create inodes for them. The inode of the file we were @@ -639,16 +711,15 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, struct key *key) { struct afs_lookup_cookie *cookie; - struct afs_cb_interest *dcbi, *cbi = NULL; - struct afs_super_info *as = dir->i_sb->s_fs_info; - struct afs_status_cb *scb; - struct afs_iget_data iget_data; - struct afs_operation fc; + struct afs_cb_interest *dcbi; + struct afs_vnode_param *vp; + struct afs_operation *op; struct afs_server *server; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; struct inode *inode = NULL, *ti; afs_dataversion_t data_version = READ_ONCE(dvnode->status.data_version); - int ret, i; + long ret; + int i; _enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry); @@ -656,6 +727,8 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, if (!cookie) return ERR_PTR(-ENOMEM); + for (i = 0; i < ARRAY_SIZE(cookie->fids); i++) + cookie->fids[i].vid = dvnode->fid.vid; cookie->ctx.actor = afs_lookup_filldir; cookie->name = dentry->d_name; cookie->nr_fids = 2; /* slot 0 is saved for the fid we actually want @@ -672,61 +745,64 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, } read_sequnlock_excl(&dvnode->cb_lock); - for (i = 0; i < 50; i++) - cookie->fids[i].vid = as->volume->vid; - /* search the directory */ ret = afs_dir_iterate(dir, &cookie->ctx, key, &data_version); - if (ret < 0) { - inode = ERR_PTR(ret); + if (ret < 0) goto out; - } dentry->d_fsdata = (void *)(unsigned long)data_version; - inode = ERR_PTR(-ENOENT); + ret = -ENOENT; if (!cookie->found) goto out; /* Check to see if we already have an inode for the primary fid. */ - iget_data.fid = cookie->fids[0]; - iget_data.volume = dvnode->volume; - iget_data.cb_v_break = dvnode->volume->cb_v_break; - iget_data.cb_s_break = 0; - inode = ilookup5(dir->i_sb, cookie->fids[0].vnode, - afs_iget5_test, &iget_data); + inode = ilookup5(dir->i_sb, cookie->fids[1].vnode, + afs_ilookup5_test_by_fid, &cookie->fids[1]); if (inode) - goto out; + goto out; /* We do */ - /* Need space for examining all the selected files */ - inode = ERR_PTR(-ENOMEM); - cookie->statuses = kvcalloc(cookie->nr_fids, sizeof(struct afs_status_cb), - GFP_KERNEL); - if (!cookie->statuses) + /* Okay, we didn't find it. We need to query the server - and whilst + * we're doing that, we're going to attempt to look up a bunch of other + * vnodes also. + */ + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) { + ret = PTR_ERR(op); goto out; + } - cookie->inodes = kcalloc(cookie->nr_fids, sizeof(struct inode *), - GFP_KERNEL); - if (!cookie->inodes) - goto out_s; - - cookie->fids[1] = dvnode->fid; - cookie->statuses[1].cb_break = afs_calc_vnode_cb_break(dvnode); - cookie->inodes[1] = igrab(&dvnode->vfs_inode); + afs_op_set_vnode(op, 0, dvnode); + afs_op_set_fid(op, 1, &cookie->fids[1]); - for (i = 2; i < cookie->nr_fids; i++) { - scb = &cookie->statuses[i]; + op->nr_files = cookie->nr_fids; + _debug("nr_files %u", op->nr_files); - /* Find any inodes that already exist and get their - * callback counters. - */ - iget_data.fid = cookie->fids[i]; - ti = ilookup5_nowait(dir->i_sb, iget_data.fid.vnode, - afs_iget5_test, &iget_data); - if (!IS_ERR_OR_NULL(ti)) { - vnode = AFS_FS_I(ti); - scb->cb_break = afs_calc_vnode_cb_break(vnode); - cookie->inodes[i] = ti; + /* Need space for examining all the selected files */ + op->error = -ENOMEM; + if (op->nr_files > 2) { + op->more_files = kvcalloc(op->nr_files - 2, + sizeof(struct afs_vnode_param), + GFP_KERNEL); + if (!op->more_files) + goto out_op; + + for (i = 2; i < op->nr_files; i++) { + vp = &op->more_files[i - 2]; + vp->fid = cookie->fids[i]; + + /* Find any inodes that already exist and get their + * callback counters. + */ + ti = ilookup5_nowait(dir->i_sb, vp->fid.vnode, + afs_ilookup5_test_by_fid, &vp->fid); + if (!IS_ERR_OR_NULL(ti)) { + vnode = AFS_FS_I(ti); + vp->dv_before = vnode->status.data_version; + vp->cb_break_before = afs_calc_vnode_cb_break(vnode); + vp->vnode = vnode; + vp->put_vnode = true; + } } } @@ -734,120 +810,40 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, * lookups contained therein are stored in the reply without aborting * the whole operation. */ - if (cookie->one_only) - goto no_inline_bulk_status; - - inode = ERR_PTR(-ERESTARTSYS); - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - while (afs_select_fileserver(&fc)) { - if (test_bit(AFS_SERVER_FL_NO_IBULK, - &fc.cbi->server->flags)) { - fc.ac.abort_code = RX_INVALID_OPERATION; - fc.ac.error = -ECONNABORTED; - break; - } - iget_data.cb_v_break = dvnode->volume->cb_v_break; - iget_data.cb_s_break = fc.cbi->server->cb_s_break; - afs_fs_inline_bulk_status(&fc, - afs_v2net(dvnode), - cookie->fids, - cookie->statuses, - cookie->nr_fids, NULL); - } - - if (fc.ac.error == 0) - cbi = afs_get_cb_interest(fc.cbi); - if (fc.ac.abort_code == RX_INVALID_OPERATION) - set_bit(AFS_SERVER_FL_NO_IBULK, &fc.cbi->server->flags); - inode = ERR_PTR(afs_end_vnode_operation(&fc)); + op->error = -ENOTSUPP; + if (!cookie->one_only) { + op->ops = &afs_inline_bulk_status_operation; + afs_begin_vnode_operation(op); + afs_wait_for_operation(op); } - if (!IS_ERR(inode)) - goto success; - if (fc.ac.abort_code != RX_INVALID_OPERATION) - goto out_c; - -no_inline_bulk_status: - /* We could try FS.BulkStatus next, but this aborts the entire op if - * any of the lookups fails - so, for the moment, revert to - * FS.FetchStatus for just the primary fid. - */ - inode = ERR_PTR(-ERESTARTSYS); - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - while (afs_select_fileserver(&fc)) { - iget_data.cb_v_break = dvnode->volume->cb_v_break; - iget_data.cb_s_break = fc.cbi->server->cb_s_break; - scb = &cookie->statuses[0]; - afs_fs_fetch_status(&fc, - afs_v2net(dvnode), - cookie->fids, - scb, - NULL); - } - - if (fc.ac.error == 0) - cbi = afs_get_cb_interest(fc.cbi); - inode = ERR_PTR(afs_end_vnode_operation(&fc)); + if (op->error == -ENOTSUPP) { + /* We could try FS.BulkStatus next, but this aborts the entire + * op if any of the lookups fails - so, for the moment, revert + * to FS.FetchStatus for op->file[1]. + */ + op->fetch_status.which = 1; + op->ops = &afs_fetch_status_operation; + afs_begin_vnode_operation(op); + afs_wait_for_operation(op); } + inode = ERR_PTR(op->error); - if (IS_ERR(inode)) - goto out_c; - -success: - /* Turn all the files into inodes and save the first one - which is the - * one we actually want. - */ - scb = &cookie->statuses[0]; - if (scb->status.abort_code != 0) - inode = ERR_PTR(afs_abort_to_error(scb->status.abort_code)); - - for (i = 0; i < cookie->nr_fids; i++) { - struct afs_status_cb *scb = &cookie->statuses[i]; - - if (!scb->have_status && !scb->have_error) - continue; - - if (cookie->inodes[i]) { - struct afs_vnode *iv = AFS_FS_I(cookie->inodes[i]); - - if (test_bit(AFS_VNODE_UNSET, &iv->flags)) - continue; - - afs_vnode_commit_status(&fc, iv, - scb->cb_break, NULL, scb); - continue; - } - - if (scb->status.abort_code != 0) - continue; - - iget_data.fid = cookie->fids[i]; - ti = afs_iget(dir->i_sb, key, &iget_data, scb, cbi, dvnode); - if (!IS_ERR(ti)) - afs_cache_permit(AFS_FS_I(ti), key, - 0 /* Assume vnode->cb_break is 0 */ + - iget_data.cb_v_break, - scb); - if (i == 0) { - inode = ti; - } else { - if (!IS_ERR(ti)) - iput(ti); - } +out_op: + if (op->error == 0) { + inode = &op->file[1].vnode->vfs_inode; + op->file[1].vnode = NULL; } -out_c: - afs_put_cb_interest(afs_v2net(dvnode), cbi); - if (cookie->inodes) { - for (i = 0; i < cookie->nr_fids; i++) - iput(cookie->inodes[i]); - kfree(cookie->inodes); - } -out_s: - kvfree(cookie->statuses); + if (op->file[0].scb.have_status) + dentry->d_fsdata = (void *)(unsigned long)op->file[0].scb.status.data_version; + else + dentry->d_fsdata = (void *)(unsigned long)op->file[0].dv_before; + ret = afs_put_operation(op); out: kfree(cookie); - return inode; + _leave(""); + return inode ?: ERR_PTR(ret); } /* @@ -963,6 +959,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, if (!IS_ERR_OR_NULL(inode)) fid = AFS_FS_I(inode)->fid; + _debug("splice %px", dentry->d_inode); d = d_splice_alias(inode, dentry); if (!IS_ERR_OR_NULL(d)) { d->d_fsdata = dentry->d_fsdata; @@ -970,6 +967,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, } else { trace_afs_lookup(dvnode, &dentry->d_name, &fid); } + _leave(""); return d; } @@ -1220,130 +1218,97 @@ void afs_d_release(struct dentry *dentry) /* * Create a new inode for create/mkdir/symlink */ -static void afs_vnode_new_inode(struct afs_operation *fc, - struct dentry *new_dentry, - struct afs_iget_data *new_data, - struct afs_status_cb *new_scb) +static void afs_vnode_new_inode(struct afs_operation *op) { + struct afs_vnode_param *vp = &op->file[1]; struct afs_vnode *vnode; struct inode *inode; - if (fc->ac.error < 0) - return; + _enter(""); + + ASSERTCMP(op->error, ==, 0); - inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key, - new_data, new_scb, fc->cbi, fc->vnode); + inode = afs_iget(op, vp); if (IS_ERR(inode)) { /* ENOMEM or EINTR at a really inconvenient time - just abandon * the new directory on the server. */ - fc->ac.error = PTR_ERR(inode); + op->error = PTR_ERR(inode); return; } vnode = AFS_FS_I(inode); set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); - if (fc->ac.error == 0) - afs_cache_permit(vnode, fc->key, vnode->cb_break, new_scb); - d_instantiate(new_dentry, inode); + if (!op->error) + afs_cache_permit(vnode, op->key, vnode->cb_break, &vp->scb); + d_instantiate(op->dentry, inode); } -static void afs_prep_for_new_inode(struct afs_operation *fc, - struct afs_iget_data *iget_data) +static void afs_create_success(struct afs_operation *op) { - iget_data->volume = fc->vnode->volume; - iget_data->cb_v_break = fc->vnode->volume->cb_v_break; - iget_data->cb_s_break = fc->cbi->server->cb_s_break; + _enter("op=%08x", op->debug_id); + afs_check_for_remote_deletion(op, op->file[0].vnode); + afs_vnode_commit_status(op, &op->file[0]); + afs_update_dentry_version(op, &op->file[0], op->dentry); + afs_vnode_new_inode(op); } -/* - * Note that a dentry got changed. We need to set d_fsdata to the data version - * number derived from the result of the operation. It doesn't matter if - * d_fsdata goes backwards as we'll just revalidate. - */ -static void afs_update_dentry_version(struct afs_operation *fc, - struct dentry *dentry, - struct afs_status_cb *scb) +static void afs_create_edit_dir(struct afs_operation *op) { - if (fc->ac.error == 0) - dentry->d_fsdata = - (void *)(unsigned long)scb->status.data_version; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; + struct afs_vnode *dvnode = dvp->vnode; + + _enter("op=%08x", op->debug_id); + + down_write(&dvnode->validate_lock); + if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && + dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) + afs_edit_dir_add(dvnode, &op->dentry->d_name, &vp->fid, + op->create.reason); + up_write(&dvnode->validate_lock); } +static void afs_create_put(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + + if (op->error) + d_drop(op->dentry); +} + +static const struct afs_operation_ops afs_mkdir_operation = { + .issue_afs_rpc = afs_fs_make_dir, + .issue_yfs_rpc = yfs_fs_make_dir, + .success = afs_create_success, + .edit_dir = afs_create_edit_dir, + .put = afs_create_put, +}; + /* * create a directory on an AFS filesystem */ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { - struct afs_iget_data iget_data; - struct afs_status_cb *scb; - struct afs_operation fc; + struct afs_operation *op; struct afs_vnode *dvnode = AFS_FS_I(dir); - struct key *key; - afs_dataversion_t data_version; - int ret; - - mode |= S_IFDIR; _enter("{%llx:%llu},{%pd},%ho", dvnode->fid.vid, dvnode->fid.vnode, dentry, mode); - ret = -ENOMEM; - scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - goto error; - - key = afs_request_key(dvnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error_scb; - } - - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - data_version = dvnode->status.data_version + 1; - - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(dvnode); - afs_prep_for_new_inode(&fc, &iget_data); - afs_fs_create(&fc, dentry->d_name.name, mode, - &scb[0], &iget_data.fid, &scb[1]); - } - - afs_check_for_remote_deletion(&fc, dvnode); - afs_vnode_commit_status(&fc, dvnode, fc.cb_break, - &data_version, &scb[0]); - afs_update_dentry_version(&fc, dentry, &scb[0]); - afs_vnode_new_inode(&fc, dentry, &iget_data, &scb[1]); - ret = afs_end_vnode_operation(&fc); - if (ret < 0) - goto error_key; - } else { - goto error_key; - } - - if (ret == 0) { - down_write(&dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && - dvnode->status.data_version == data_version) - afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid, - afs_edit_dir_for_create); - up_write(&dvnode->validate_lock); + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) { + d_drop(dentry); + return PTR_ERR(op); } - key_put(key); - kfree(scb); - _leave(" = 0"); - return 0; - -error_key: - key_put(key); -error_scb: - kfree(scb); -error: - d_drop(dentry); - _leave(" = %d", ret); - return ret; + afs_op_set_vnode(op, 0, dvnode); + op->file[0].dv_delta = 1; + op->dentry = dentry; + op->create.mode = S_IFDIR | mode; + op->create.reason = afs_edit_dir_for_mkdir; + op->ops = &afs_mkdir_operation; + return afs_do_sync_operation(op); } /* @@ -1361,76 +1326,86 @@ static void afs_dir_remove_subdir(struct dentry *dentry) } } +static void afs_rmdir_success(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + afs_check_for_remote_deletion(op, op->file[0].vnode); + afs_vnode_commit_status(op, &op->file[0]); + afs_update_dentry_version(op, &op->file[0], op->dentry); +} + +static void afs_rmdir_edit_dir(struct afs_operation *op) +{ + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode *dvnode = dvp->vnode; + + _enter("op=%08x", op->debug_id); + afs_dir_remove_subdir(op->dentry); + + down_write(&dvnode->validate_lock); + if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && + dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) + afs_edit_dir_remove(dvnode, &op->dentry->d_name, + afs_edit_dir_for_rmdir); + up_write(&dvnode->validate_lock); +} + +static void afs_rmdir_put(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + if (op->file[1].vnode) + up_write(&op->file[1].vnode->rmdir_lock); +} + +static const struct afs_operation_ops afs_rmdir_operation = { + .issue_afs_rpc = afs_fs_remove_dir, + .issue_yfs_rpc = yfs_fs_remove_dir, + .success = afs_rmdir_success, + .edit_dir = afs_rmdir_edit_dir, + .put = afs_rmdir_put, +}; + /* * remove a directory from an AFS filesystem */ static int afs_rmdir(struct inode *dir, struct dentry *dentry) { - struct afs_status_cb *scb; - struct afs_operation fc; + struct afs_operation *op; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL; - struct key *key; - afs_dataversion_t data_version; int ret; _enter("{%llx:%llu},{%pd}", dvnode->fid.vid, dvnode->fid.vnode, dentry); - scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - return -ENOMEM; + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - key = afs_request_key(dvnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error; - } + afs_op_set_vnode(op, 0, dvnode); + op->file[0].dv_delta = 1; + + op->dentry = dentry; + op->ops = &afs_rmdir_operation; /* Try to make sure we have a callback promise on the victim. */ if (d_really_is_positive(dentry)) { vnode = AFS_FS_I(d_inode(dentry)); - ret = afs_validate(vnode, key); + ret = afs_validate(vnode, op->key); if (ret < 0) - goto error_key; + goto error; } if (vnode) { ret = down_write_killable(&vnode->rmdir_lock); if (ret < 0) - goto error_key; + goto error; + op->file[1].vnode = vnode; } - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - data_version = dvnode->status.data_version + 1; - - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(dvnode); - afs_fs_remove(&fc, vnode, dentry->d_name.name, true, scb); - } - - afs_vnode_commit_status(&fc, dvnode, fc.cb_break, - &data_version, scb); - afs_update_dentry_version(&fc, dentry, scb); - ret = afs_end_vnode_operation(&fc); - if (ret == 0) { - afs_dir_remove_subdir(dentry); - down_write(&dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && - dvnode->status.data_version == data_version) - afs_edit_dir_remove(dvnode, &dentry->d_name, - afs_edit_dir_for_rmdir); - up_write(&dvnode->validate_lock); - } - } + return afs_do_sync_operation(op); - if (vnode) - up_write(&vnode->rmdir_lock); -error_key: - key_put(key); error: - kfree(scb); - return ret; + return afs_put_operation(op); } /* @@ -1443,52 +1418,90 @@ error: * However, if we didn't have a callback promise outstanding, or it was * outstanding on a different server, then it won't break it either... */ -static int afs_dir_remove_link(struct afs_vnode *dvnode, struct dentry *dentry, - struct key *key) +static void afs_dir_remove_link(struct afs_operation *op) { - int ret = 0; + struct afs_vnode *dvnode = op->file[0].vnode; + struct afs_vnode *vnode = op->file[1].vnode; + struct dentry *dentry = op->dentry; + int ret; - if (d_really_is_positive(dentry)) { - struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); + if (op->error != 0 || + (op->file[1].scb.have_status && op->file[1].scb.have_error)) + return; + if (d_really_is_positive(dentry)) + return; - if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { - /* Already done */ - } else if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) { - write_seqlock(&vnode->cb_lock); - drop_nlink(&vnode->vfs_inode); - if (vnode->vfs_inode.i_nlink == 0) { - set_bit(AFS_VNODE_DELETED, &vnode->flags); - __afs_break_callback(vnode, afs_cb_break_for_unlink); - } - write_sequnlock(&vnode->cb_lock); - ret = 0; - } else { - afs_break_callback(vnode, afs_cb_break_for_unlink); + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { + /* Already done */ + } else if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) { + write_seqlock(&vnode->cb_lock); + drop_nlink(&vnode->vfs_inode); + if (vnode->vfs_inode.i_nlink == 0) { + set_bit(AFS_VNODE_DELETED, &vnode->flags); + __afs_break_callback(vnode, afs_cb_break_for_unlink); + } + write_sequnlock(&vnode->cb_lock); + } else { + afs_break_callback(vnode, afs_cb_break_for_unlink); - if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) - kdebug("AFS_VNODE_DELETED"); + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) + _debug("AFS_VNODE_DELETED"); - ret = afs_validate(vnode, key); - if (ret == -ESTALE) - ret = 0; - } - _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret); + ret = afs_validate(vnode, op->key); + if (ret != -ESTALE) + op->error = ret; } - return ret; + _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, op->error); +} + +static void afs_unlink_success(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + afs_check_for_remote_deletion(op, op->file[0].vnode); + afs_vnode_commit_status(op, &op->file[0]); + afs_vnode_commit_status(op, &op->file[1]); + afs_update_dentry_version(op, &op->file[0], op->dentry); + afs_dir_remove_link(op); +} + +static void afs_unlink_edit_dir(struct afs_operation *op) +{ + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode *dvnode = dvp->vnode; + + _enter("op=%08x", op->debug_id); + down_write(&dvnode->validate_lock); + if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && + dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) + afs_edit_dir_remove(dvnode, &op->dentry->d_name, + afs_edit_dir_for_unlink); + up_write(&dvnode->validate_lock); } +static void afs_unlink_put(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + if (op->unlink.need_rehash && op->error < 0 && op->error != -ENOENT) + d_rehash(op->dentry); +} + +static const struct afs_operation_ops afs_unlink_operation = { + .issue_afs_rpc = afs_fs_remove_file, + .issue_yfs_rpc = yfs_fs_remove_file, + .success = afs_unlink_success, + .edit_dir = afs_unlink_edit_dir, + .put = afs_unlink_put, +}; + /* * Remove a file or symlink from an AFS filesystem. */ static int afs_unlink(struct inode *dir, struct dentry *dentry) { - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *dvnode = AFS_FS_I(dir); struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); - struct key *key; - bool need_rehash = false; int ret; _enter("{%llx:%llu},{%pd}", @@ -1497,269 +1510,176 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) if (dentry->d_name.len >= AFSNAMEMAX) return -ENAMETOOLONG; - ret = -ENOMEM; - scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - goto error; + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - key = afs_request_key(dvnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error_scb; - } + afs_op_set_vnode(op, 0, dvnode); + op->file[0].dv_delta = 1; /* Try to make sure we have a callback promise on the victim. */ - ret = afs_validate(vnode, key); - if (ret < 0) - goto error_key; + ret = afs_validate(vnode, op->key); + if (ret < 0) { + op->error = ret; + goto error; + } spin_lock(&dentry->d_lock); if (d_count(dentry) > 1) { spin_unlock(&dentry->d_lock); /* Start asynchronous writeout of the inode */ write_inode_now(d_inode(dentry), 0); - ret = afs_sillyrename(dvnode, vnode, dentry, key); - goto error_key; + op->error = afs_sillyrename(dvnode, vnode, dentry, op->key); + goto error; } if (!d_unhashed(dentry)) { /* Prevent a race with RCU lookup. */ __d_drop(dentry); - need_rehash = true; + op->unlink.need_rehash = true; } spin_unlock(&dentry->d_lock); - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - afs_dataversion_t data_version = dvnode->status.data_version + 1; - afs_dataversion_t data_version_2 = vnode->status.data_version; - - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(dvnode); - fc.cb_break_2 = afs_calc_vnode_cb_break(vnode); - - 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, - &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, &scb[0]); - } - - afs_vnode_commit_status(&fc, dvnode, fc.cb_break, - &data_version, &scb[0]); - afs_vnode_commit_status(&fc, vnode, fc.cb_break_2, - &data_version_2, &scb[1]); - afs_update_dentry_version(&fc, dentry, &scb[0]); - ret = afs_end_vnode_operation(&fc); - if (ret == 0 && !(scb[1].have_status || scb[1].have_error)) - ret = afs_dir_remove_link(dvnode, dentry, key); - - if (ret == 0) { - down_write(&dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && - dvnode->status.data_version == data_version) - afs_edit_dir_remove(dvnode, &dentry->d_name, - afs_edit_dir_for_unlink); - up_write(&dvnode->validate_lock); - } - } - - if (need_rehash && ret < 0 && ret != -ENOENT) - d_rehash(dentry); + op->file[1].vnode = vnode; + op->dentry = dentry; + op->ops = &afs_unlink_operation; + return afs_do_sync_operation(op); -error_key: - key_put(key); -error_scb: - kfree(scb); error: - _leave(" = %d", ret); - return ret; + return afs_put_operation(op); } +static const struct afs_operation_ops afs_create_operation = { + .issue_afs_rpc = afs_fs_create_file, + .issue_yfs_rpc = yfs_fs_create_file, + .success = afs_create_success, + .edit_dir = afs_create_edit_dir, + .put = afs_create_put, +}; + /* * create a regular file on an AFS filesystem */ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { - struct afs_iget_data iget_data; - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *dvnode = AFS_FS_I(dir); - struct key *key; - afs_dataversion_t data_version; - int ret; - - mode |= S_IFREG; + int ret = -ENAMETOOLONG; - _enter("{%llx:%llu},{%pd},%ho,", + _enter("{%llx:%llu},{%pd},%ho", dvnode->fid.vid, dvnode->fid.vnode, dentry, mode); - ret = -ENAMETOOLONG; if (dentry->d_name.len >= AFSNAMEMAX) goto error; - key = afs_request_key(dvnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) { + ret = PTR_ERR(op); goto error; } - ret = -ENOMEM; - scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - goto error_scb; - - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - data_version = dvnode->status.data_version + 1; - - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(dvnode); - afs_prep_for_new_inode(&fc, &iget_data); - afs_fs_create(&fc, dentry->d_name.name, mode, - &scb[0], &iget_data.fid, &scb[1]); - } - - afs_check_for_remote_deletion(&fc, dvnode); - afs_vnode_commit_status(&fc, dvnode, fc.cb_break, - &data_version, &scb[0]); - afs_update_dentry_version(&fc, dentry, &scb[0]); - afs_vnode_new_inode(&fc, dentry, &iget_data, &scb[1]); - ret = afs_end_vnode_operation(&fc); - if (ret < 0) - goto error_key; - } else { - goto error_key; - } - - down_write(&dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && - dvnode->status.data_version == data_version) - afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid, - afs_edit_dir_for_create); - up_write(&dvnode->validate_lock); + afs_op_set_vnode(op, 0, dvnode); + op->file[0].dv_delta = 1; - kfree(scb); - key_put(key); - _leave(" = 0"); - return 0; + op->dentry = dentry; + op->create.mode = S_IFREG | mode; + op->create.reason = afs_edit_dir_for_create; + op->ops = &afs_create_operation; + return afs_do_sync_operation(op); -error_scb: - kfree(scb); -error_key: - key_put(key); error: d_drop(dentry); _leave(" = %d", ret); return ret; } +static void afs_link_success(struct afs_operation *op) +{ + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; + + _enter("op=%08x", op->debug_id); + afs_vnode_commit_status(op, dvp); + afs_vnode_commit_status(op, vp); + afs_update_dentry_version(op, dvp, op->dentry); + if (op->dentry_2->d_parent == op->dentry->d_parent) + afs_update_dentry_version(op, dvp, op->dentry_2); + ihold(&vp->vnode->vfs_inode); + d_instantiate(op->dentry, &vp->vnode->vfs_inode); +} + +static void afs_link_put(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + if (op->error) + d_drop(op->dentry); +} + +static const struct afs_operation_ops afs_link_operation = { + .issue_afs_rpc = afs_fs_link, + .issue_yfs_rpc = yfs_fs_link, + .success = afs_link_success, + .edit_dir = afs_create_edit_dir, + .put = afs_link_put, +}; + /* * create a hard link between files in an AFS filesystem */ static int afs_link(struct dentry *from, struct inode *dir, struct dentry *dentry) { - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *dvnode = AFS_FS_I(dir); struct afs_vnode *vnode = AFS_FS_I(d_inode(from)); - struct key *key; - afs_dataversion_t data_version; - int ret; + int ret = -ENAMETOOLONG; _enter("{%llx:%llu},{%llx:%llu},{%pd}", vnode->fid.vid, vnode->fid.vnode, dvnode->fid.vid, dvnode->fid.vnode, dentry); - ret = -ENAMETOOLONG; if (dentry->d_name.len >= AFSNAMEMAX) goto error; - ret = -ENOMEM; - scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) { + ret = PTR_ERR(op); goto error; - - key = afs_request_key(dvnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error_scb; } - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - data_version = dvnode->status.data_version + 1; - - if (mutex_lock_interruptible_nested(&vnode->io_lock, 1) < 0) { - afs_end_vnode_operation(&fc); - goto error_key; - } - - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(dvnode); - fc.cb_break_2 = afs_calc_vnode_cb_break(vnode); - afs_fs_link(&fc, vnode, dentry->d_name.name, - &scb[0], &scb[1]); - } - - afs_vnode_commit_status(&fc, dvnode, fc.cb_break, - &data_version, &scb[0]); - afs_vnode_commit_status(&fc, vnode, fc.cb_break_2, - NULL, &scb[1]); - ihold(&vnode->vfs_inode); - afs_update_dentry_version(&fc, dentry, &scb[0]); - d_instantiate(dentry, &vnode->vfs_inode); - - mutex_unlock(&vnode->io_lock); - ret = afs_end_vnode_operation(&fc); - if (ret < 0) - goto error_key; - } else { - goto error_key; - } + afs_op_set_vnode(op, 0, dvnode); + afs_op_set_vnode(op, 1, vnode); + op->file[0].dv_delta = 1; - down_write(&dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && - dvnode->status.data_version == data_version) - afs_edit_dir_add(dvnode, &dentry->d_name, &vnode->fid, - afs_edit_dir_for_link); - up_write(&dvnode->validate_lock); + op->dentry = dentry; + op->dentry_2 = from; + op->ops = &afs_link_operation; + op->create.reason = afs_edit_dir_for_link; + return afs_do_sync_operation(op); - key_put(key); - kfree(scb); - _leave(" = 0"); - return 0; - -error_key: - key_put(key); -error_scb: - kfree(scb); error: d_drop(dentry); _leave(" = %d", ret); return ret; } +static const struct afs_operation_ops afs_symlink_operation = { + .issue_afs_rpc = afs_fs_symlink, + .issue_yfs_rpc = yfs_fs_symlink, + .success = afs_create_success, + .edit_dir = afs_create_edit_dir, + .put = afs_create_put, +}; + /* * create a symlink in an AFS filesystem */ static int afs_symlink(struct inode *dir, struct dentry *dentry, const char *content) { - struct afs_iget_data iget_data; - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *dvnode = AFS_FS_I(dir); - struct key *key; - afs_dataversion_t data_version; int ret; _enter("{%llx:%llu},{%pd},%s", @@ -1774,62 +1694,115 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, if (strlen(content) >= AFSPATHMAX) goto error; - ret = -ENOMEM; - scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) { + ret = PTR_ERR(op); goto error; - - key = afs_request_key(dvnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error_scb; } - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - data_version = dvnode->status.data_version + 1; - - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(dvnode); - afs_prep_for_new_inode(&fc, &iget_data); - afs_fs_symlink(&fc, dentry->d_name.name, content, - &scb[0], &iget_data.fid, &scb[1]); - } + afs_op_set_vnode(op, 0, dvnode); + op->file[0].dv_delta = 1; - afs_check_for_remote_deletion(&fc, dvnode); - afs_vnode_commit_status(&fc, dvnode, fc.cb_break, - &data_version, &scb[0]); - afs_update_dentry_version(&fc, dentry, &scb[0]); - afs_vnode_new_inode(&fc, dentry, &iget_data, &scb[1]); - ret = afs_end_vnode_operation(&fc); - if (ret < 0) - goto error_key; - } else { - goto error_key; - } - - down_write(&dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && - dvnode->status.data_version == data_version) - afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid, - afs_edit_dir_for_symlink); - up_write(&dvnode->validate_lock); - - key_put(key); - kfree(scb); - _leave(" = 0"); - return 0; + op->dentry = dentry; + op->ops = &afs_symlink_operation; + op->create.reason = afs_edit_dir_for_symlink; + op->create.symlink = content; + return afs_do_sync_operation(op); -error_key: - key_put(key); -error_scb: - kfree(scb); error: d_drop(dentry); _leave(" = %d", ret); return ret; } +static void afs_rename_success(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + + afs_vnode_commit_status(op, &op->file[0]); + if (op->file[1].vnode != op->file[0].vnode) + afs_vnode_commit_status(op, &op->file[1]); +} + +static void afs_rename_edit_dir(struct afs_operation *op) +{ + struct afs_vnode_param *orig_dvp = &op->file[0]; + struct afs_vnode_param *new_dvp = &op->file[1]; + struct afs_vnode *orig_dvnode = orig_dvp->vnode; + struct afs_vnode *new_dvnode = new_dvp->vnode; + struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry)); + struct dentry *old_dentry = op->dentry; + struct dentry *new_dentry = op->dentry_2; + struct inode *new_inode; + + _enter("op=%08x", op->debug_id); + + if (op->rename.rehash) { + d_rehash(op->rename.rehash); + op->rename.rehash = NULL; + } + + down_write(&orig_dvnode->validate_lock); + if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags) && + orig_dvnode->status.data_version == orig_dvp->dv_before + orig_dvp->dv_delta) + afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name, + afs_edit_dir_for_rename_0); + + if (new_dvnode != orig_dvnode) { + up_write(&orig_dvnode->validate_lock); + down_write(&new_dvnode->validate_lock); + } + + if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags) && + new_dvnode->status.data_version == new_dvp->dv_before + new_dvp->dv_delta) { + if (!op->rename.new_negative) + afs_edit_dir_remove(new_dvnode, &new_dentry->d_name, + afs_edit_dir_for_rename_1); + + afs_edit_dir_add(new_dvnode, &new_dentry->d_name, + &vnode->fid, afs_edit_dir_for_rename_2); + } + + new_inode = d_inode(new_dentry); + if (new_inode) { + spin_lock(&new_inode->i_lock); + if (new_inode->i_nlink > 0) + drop_nlink(new_inode); + spin_unlock(&new_inode->i_lock); + } + + /* Now we can update d_fsdata on the dentries to reflect their + * new parent's data_version. + * + * Note that if we ever implement RENAME_EXCHANGE, we'll have + * to update both dentries with opposing dir versions. + */ + afs_update_dentry_version(op, new_dvp, op->dentry); + afs_update_dentry_version(op, new_dvp, op->dentry_2); + + d_move(old_dentry, new_dentry); + + up_write(&new_dvnode->validate_lock); +} + +static void afs_rename_put(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + if (op->rename.rehash) + d_rehash(op->rename.rehash); + dput(op->rename.tmp); + if (op->error) + d_rehash(op->dentry); +} + +static const struct afs_operation_ops afs_rename_operation = { + .issue_afs_rpc = afs_fs_rename, + .issue_yfs_rpc = yfs_fs_rename, + .success = afs_rename_success, + .edit_dir = afs_rename_edit_dir, + .put = afs_rename_put, +}; + /* * rename a file in an AFS filesystem and/or move it between directories */ @@ -1837,15 +1810,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; - struct dentry *tmp = NULL, *rehash = NULL; - struct inode *new_inode; - struct key *key; - afs_dataversion_t orig_data_version; - afs_dataversion_t new_data_version; - bool new_negative = d_is_negative(new_dentry); int ret; if (flags) @@ -1865,16 +1831,19 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, new_dvnode->fid.vid, new_dvnode->fid.vnode, new_dentry); - ret = -ENOMEM; - scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - goto error; + op = afs_alloc_operation(NULL, orig_dvnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - key = afs_request_key(orig_dvnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error_scb; - } + afs_op_set_vnode(op, 0, orig_dvnode); + afs_op_set_vnode(op, 1, new_dvnode); /* May be same as orig_dvnode */ + op->file[0].dv_delta = 1; + op->file[1].dv_delta = 1; + + op->dentry = old_dentry; + op->dentry_2 = new_dentry; + op->rename.new_negative = d_is_negative(new_dentry); + op->ops = &afs_rename_operation; /* For non-directories, check whether the target is busy and if so, * make a copy of the dentry and then do a silly-rename. If the @@ -1887,26 +1856,26 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, */ if (!d_unhashed(new_dentry)) { d_drop(new_dentry); - rehash = new_dentry; + op->rename.rehash = new_dentry; } if (d_count(new_dentry) > 2) { /* copy the target dentry's name */ ret = -ENOMEM; - tmp = d_alloc(new_dentry->d_parent, - &new_dentry->d_name); - if (!tmp) - goto error_rehash; + op->rename.tmp = d_alloc(new_dentry->d_parent, + &new_dentry->d_name); + if (!op->rename.tmp) + goto error; ret = afs_sillyrename(new_dvnode, AFS_FS_I(d_inode(new_dentry)), - new_dentry, key); + new_dentry, op->key); if (ret) - goto error_rehash; + goto error; - new_dentry = tmp; - rehash = NULL; - new_negative = true; + op->dentry_2 = op->rename.tmp; + op->rename.rehash = NULL; + op->rename.new_negative = true; } } @@ -1921,98 +1890,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, */ d_drop(old_dentry); - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, orig_dvnode, key, true)) { - orig_data_version = orig_dvnode->status.data_version + 1; - - if (orig_dvnode != new_dvnode) { - if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) { - afs_end_vnode_operation(&fc); - goto error_rehash_old; - } - new_data_version = new_dvnode->status.data_version + 1; - } else { - new_data_version = orig_data_version; - } - - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(orig_dvnode); - fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode); - afs_fs_rename(&fc, old_dentry->d_name.name, - new_dvnode, new_dentry->d_name.name, - &scb[0], &scb[1]); - } - - afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break, - &orig_data_version, &scb[0]); - if (new_dvnode != orig_dvnode) { - afs_vnode_commit_status(&fc, new_dvnode, fc.cb_break_2, - &new_data_version, &scb[1]); - mutex_unlock(&new_dvnode->io_lock); - } - ret = afs_end_vnode_operation(&fc); - if (ret < 0) - goto error_rehash_old; - } - - if (ret == 0) { - if (rehash) - d_rehash(rehash); - down_write(&orig_dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags) && - orig_dvnode->status.data_version == orig_data_version) - afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name, - afs_edit_dir_for_rename_0); - if (orig_dvnode != new_dvnode) { - up_write(&orig_dvnode->validate_lock); - - down_write(&new_dvnode->validate_lock); - } - if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags) && - orig_dvnode->status.data_version == new_data_version) { - if (!new_negative) - afs_edit_dir_remove(new_dvnode, &new_dentry->d_name, - afs_edit_dir_for_rename_1); - - afs_edit_dir_add(new_dvnode, &new_dentry->d_name, - &vnode->fid, afs_edit_dir_for_rename_2); - } - - new_inode = d_inode(new_dentry); - if (new_inode) { - spin_lock(&new_inode->i_lock); - if (new_inode->i_nlink > 0) - drop_nlink(new_inode); - spin_unlock(&new_inode->i_lock); - } - - /* Now we can update d_fsdata on the dentries to reflect their - * new parent's data_version. - * - * Note that if we ever implement RENAME_EXCHANGE, we'll have - * to update both dentries with opposing dir versions. - */ - afs_update_dentry_version(&fc, old_dentry, &scb[1]); - afs_update_dentry_version(&fc, new_dentry, &scb[1]); - d_move(old_dentry, new_dentry); - up_write(&new_dvnode->validate_lock); - goto error_tmp; - } + return afs_do_sync_operation(op); -error_rehash_old: - d_rehash(new_dentry); -error_rehash: - if (rehash) - d_rehash(rehash); -error_tmp: - if (tmp) - dput(tmp); - key_put(key); -error_scb: - kfree(scb); error: - _leave(" = %d", ret); - return ret; + return afs_put_operation(op); } /* diff --git a/fs/afs/dir_silly.c b/fs/afs/dir_silly.c index 0a82b134aa0d..b14e3d9a25e2 100644 --- a/fs/afs/dir_silly.c +++ b/fs/afs/dir_silly.c @@ -12,6 +12,47 @@ #include #include "internal.h" +static void afs_silly_rename_success(struct afs_operation *op) +{ + _enter("op=%08x", op->debug_id); + + afs_vnode_commit_status(op, &op->file[0]); +} + +static void afs_silly_rename_edit_dir(struct afs_operation *op) +{ + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode *dvnode = dvp->vnode; + struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry)); + struct dentry *old = op->dentry; + struct dentry *new = op->dentry_2; + + spin_lock(&old->d_lock); + old->d_flags |= DCACHE_NFSFS_RENAMED; + spin_unlock(&old->d_lock); + if (dvnode->silly_key != op->key) { + key_put(dvnode->silly_key); + dvnode->silly_key = key_get(op->key); + } + + down_write(&dvnode->validate_lock); + if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && + dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) { + afs_edit_dir_remove(dvnode, &old->d_name, + afs_edit_dir_for_silly_0); + afs_edit_dir_add(dvnode, &new->d_name, + &vnode->fid, afs_edit_dir_for_silly_1); + } + up_write(&dvnode->validate_lock); +} + +static const struct afs_operation_ops afs_silly_rename_operation = { + .issue_afs_rpc = afs_fs_rename, + .issue_yfs_rpc = yfs_fs_rename, + .success = afs_silly_rename_success, + .edit_dir = afs_silly_rename_edit_dir, +}; + /* * Actually perform the silly rename step. */ @@ -19,56 +60,22 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode struct dentry *old, struct dentry *new, struct key *key) { - struct afs_operation fc; - struct afs_status_cb *scb; - afs_dataversion_t dir_data_version; - int ret = -ERESTARTSYS; + struct afs_operation *op; _enter("%pd,%pd", old, new); - scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - return -ENOMEM; + op = afs_alloc_operation(key, dvnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - trace_afs_silly_rename(vnode, false); - if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { - 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, - scb, scb); - } - - afs_vnode_commit_status(&fc, dvnode, fc.cb_break, - &dir_data_version, scb); - ret = afs_end_vnode_operation(&fc); - } + afs_op_set_vnode(op, 0, dvnode); - if (ret == 0) { - spin_lock(&old->d_lock); - old->d_flags |= DCACHE_NFSFS_RENAMED; - spin_unlock(&old->d_lock); - if (dvnode->silly_key != key) { - key_put(dvnode->silly_key); - dvnode->silly_key = key_get(key); - } - - down_write(&dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && - dvnode->status.data_version == dir_data_version) { - afs_edit_dir_remove(dvnode, &old->d_name, - afs_edit_dir_for_silly_0); - afs_edit_dir_add(dvnode, &new->d_name, - &vnode->fid, afs_edit_dir_for_silly_1); - } - up_write(&dvnode->validate_lock); - } + op->dentry = old; + op->dentry_2 = new; + op->ops = &afs_silly_rename_operation; - kfree(scb); - _leave(" = %d", ret); - return ret; + trace_afs_silly_rename(vnode, false); + return afs_do_sync_operation(op); } /** @@ -139,65 +146,66 @@ out: return ret; } +static void afs_silly_unlink_success(struct afs_operation *op) +{ + struct afs_vnode *vnode = op->file[1].vnode; + + _enter("op=%08x", op->debug_id); + afs_check_for_remote_deletion(op, op->file[0].vnode); + afs_vnode_commit_status(op, &op->file[0]); + afs_vnode_commit_status(op, &op->file[1]); + afs_update_dentry_version(op, &op->file[0], op->dentry); + + drop_nlink(&vnode->vfs_inode); + if (vnode->vfs_inode.i_nlink == 0) { + set_bit(AFS_VNODE_DELETED, &vnode->flags); + clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); + } +} + +static void afs_silly_unlink_edit_dir(struct afs_operation *op) +{ + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode *dvnode = dvp->vnode; + + _enter("op=%08x", op->debug_id); + down_write(&dvnode->validate_lock); + if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && + dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) + afs_edit_dir_remove(dvnode, &op->dentry->d_name, + afs_edit_dir_for_unlink); + up_write(&dvnode->validate_lock); +} + +static const struct afs_operation_ops afs_silly_unlink_operation = { + .issue_afs_rpc = afs_fs_remove_file, + .issue_yfs_rpc = yfs_fs_remove_file, + .success = afs_silly_unlink_success, + .edit_dir = afs_silly_unlink_edit_dir, +}; + /* * Tell the server to remove a sillyrename file. */ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode, struct dentry *dentry, struct key *key) { - struct afs_operation fc; - struct afs_status_cb *scb; - int ret = -ERESTARTSYS; + struct afs_operation *op; _enter(""); - scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - return -ENOMEM; + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - 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, - &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, &scb[0]); - } + afs_op_set_vnode(op, 0, dvnode); + afs_op_set_vnode(op, 1, vnode); - 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); - if (vnode->vfs_inode.i_nlink == 0) { - set_bit(AFS_VNODE_DELETED, &vnode->flags); - clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); - } - } - if (ret == 0) { - down_write(&dvnode->validate_lock); - if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && - dvnode->status.data_version == dir_data_version) - afs_edit_dir_remove(dvnode, &dentry->d_name, - afs_edit_dir_for_unlink); - up_write(&dvnode->validate_lock); - } - } + op->dentry = dentry; + op->ops = &afs_silly_unlink_operation; - kfree(scb); - _leave(" = %d", ret); - return ret; + trace_afs_silly_rename(vnode, true); + return afs_do_sync_operation(op); } /* diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index 7503899c0a1b..b79879aacc02 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -10,6 +10,99 @@ #include #include "internal.h" +static atomic_t afs_autocell_ino; + +/* + * iget5() comparator for inode created by autocell operations + * + * These pseudo inodes don't match anything. + */ +static int afs_iget5_pseudo_test(struct inode *inode, void *opaque) +{ + return 0; +} + +/* + * iget5() inode initialiser + */ +static int afs_iget5_pseudo_set(struct inode *inode, void *opaque) +{ + struct afs_super_info *as = AFS_FS_S(inode->i_sb); + struct afs_vnode *vnode = AFS_FS_I(inode); + struct afs_fid *fid = opaque; + + vnode->volume = as->volume; + vnode->fid = *fid; + inode->i_ino = fid->vnode; + inode->i_generation = fid->unique; + return 0; +} + +/* + * Create an inode for a dynamic root directory or an autocell dynamic + * automount dir. + */ +struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root) +{ + struct afs_super_info *as = AFS_FS_S(sb); + struct afs_vnode *vnode; + struct inode *inode; + struct afs_fid fid = {}; + + _enter(""); + + if (as->volume) + fid.vid = as->volume->vid; + if (root) { + fid.vnode = 1; + fid.unique = 1; + } else { + fid.vnode = atomic_inc_return(&afs_autocell_ino); + fid.unique = 0; + } + + inode = iget5_locked(sb, fid.vnode, + afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid); + if (!inode) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + _debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }", + inode, inode->i_ino, fid.vid, fid.vnode, fid.unique); + + vnode = AFS_FS_I(inode); + + /* there shouldn't be an existing inode */ + BUG_ON(!(inode->i_state & I_NEW)); + + inode->i_size = 0; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (root) { + inode->i_op = &afs_dynroot_inode_operations; + inode->i_fop = &simple_dir_operations; + } else { + inode->i_op = &afs_autocell_inode_operations; + } + set_nlink(inode, 2); + inode->i_uid = GLOBAL_ROOT_UID; + inode->i_gid = GLOBAL_ROOT_GID; + inode->i_ctime = inode->i_atime = inode->i_mtime = current_time(inode); + inode->i_blocks = 0; + inode->i_generation = 0; + + set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); + if (!root) { + set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); + inode->i_flags |= S_AUTOMOUNT; + } + + inode->i_flags |= S_NOATIME; + unlock_new_inode(inode); + _leave(" = %p", inode); + return inode; +} + /* * Probe to see if a cell may exist. This prevents positive dentries from * being created unnecessarily. diff --git a/fs/afs/file.c b/fs/afs/file.c index 0c0ccc1412ee..506c47471b42 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -69,7 +69,7 @@ static const struct vm_operations_struct afs_vm_ops = { */ void afs_put_wb_key(struct afs_wb_key *wbk) { - if (refcount_dec_and_test(&wbk->usage)) { + if (wbk && refcount_dec_and_test(&wbk->usage)) { key_put(wbk->key); kfree(wbk); } @@ -220,14 +220,35 @@ static void afs_file_readpage_read_complete(struct page *page, } #endif +static void afs_fetch_data_success(struct afs_operation *op) +{ + struct afs_vnode *vnode = op->file[0].vnode; + + _enter("op=%08x", op->debug_id); + afs_check_for_remote_deletion(op, vnode); + afs_vnode_commit_status(op, &op->file[0]); + afs_stat_v(vnode, n_fetches); + atomic_long_add(op->fetch.req->actual_len, &op->net->n_fetch_bytes); +} + +static void afs_fetch_data_put(struct afs_operation *op) +{ + afs_put_read(op->fetch.req); +} + +static const struct afs_operation_ops afs_fetch_data_operation = { + .issue_afs_rpc = afs_fs_fetch_data, + .issue_yfs_rpc = yfs_fs_fetch_data, + .success = afs_fetch_data_success, + .put = afs_fetch_data_put, +}; + /* * Fetch file data from the volume. */ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *req) { - struct afs_operation fc; - struct afs_status_cb *scb; - int ret; + struct afs_operation *op; _enter("%s{%llx:%llu.%u},%x,,,", vnode->volume->name, @@ -236,34 +257,15 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *re 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; + op = afs_alloc_operation(key, vnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(vnode); - afs_fs_fetch_data(&fc, scb, req); - } - - 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); - } + afs_op_set_vnode(op, 0, vnode); - if (ret == 0) { - afs_stat_v(vnode, n_fetches); - atomic_long_add(req->actual_len, - &afs_v2net(vnode)->n_fetch_bytes); - } - - kfree(scb); - _leave(" = %d", ret); - return ret; + op->fetch.req = afs_get_read(req); + op->ops = &afs_fetch_data_operation; + return afs_do_sync_operation(op); } /* diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 682fe745f10e..70e518f7bc19 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c @@ -70,7 +70,8 @@ static void afs_schedule_lock_extension(struct afs_vnode *vnode) */ void afs_lock_op_done(struct afs_call *call) { - struct afs_vnode *vnode = call->lvnode; + struct afs_operation *op = call->op; + struct afs_vnode *vnode = op->lock.lvnode; if (call->error == 0) { spin_lock(&vnode->lock); @@ -172,15 +173,28 @@ static void afs_kill_lockers_enoent(struct afs_vnode *vnode) vnode->lock_key = NULL; } +static void afs_lock_success(struct afs_operation *op) +{ + struct afs_vnode *vnode = op->file[0].vnode; + + _enter("op=%08x", op->debug_id); + afs_check_for_remote_deletion(op, vnode); + afs_vnode_commit_status(op, &op->file[0]); +} + +static const struct afs_operation_ops afs_set_lock_operation = { + .issue_afs_rpc = afs_fs_set_lock, + .issue_yfs_rpc = yfs_fs_set_lock, + .success = afs_lock_success, +}; + /* * Get a lock on a file */ static int afs_set_lock(struct afs_vnode *vnode, struct key *key, afs_lock_type_t type) { - struct afs_status_cb *scb; - struct afs_operation fc; - int ret; + struct afs_operation *op; _enter("%s{%llx:%llu.%u},%x,%u", vnode->volume->name, @@ -189,35 +203,29 @@ 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; + op = afs_alloc_operation(key, vnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - 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, scb); - } - - afs_check_for_remote_deletion(&fc, vnode); - afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb); - ret = afs_end_vnode_operation(&fc); - } + afs_op_set_vnode(op, 0, vnode); - kfree(scb); - _leave(" = %d", ret); - return ret; + op->lock.type = type; + op->ops = &afs_set_lock_operation; + return afs_do_sync_operation(op); } +static const struct afs_operation_ops afs_extend_lock_operation = { + .issue_afs_rpc = afs_fs_extend_lock, + .issue_yfs_rpc = yfs_fs_extend_lock, + .success = afs_lock_success, +}; + /* * Extend a lock on a file */ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key) { - struct afs_status_cb *scb; - struct afs_operation fc; - int ret; + struct afs_operation *op; _enter("%s{%llx:%llu.%u},%x", vnode->volume->name, @@ -226,35 +234,29 @@ 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, scb); - } + op = afs_alloc_operation(key, vnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - afs_check_for_remote_deletion(&fc, vnode); - afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb); - ret = afs_end_vnode_operation(&fc); - } + afs_op_set_vnode(op, 0, vnode); - kfree(scb); - _leave(" = %d", ret); - return ret; + op->flags |= AFS_OPERATION_UNINTR; + op->ops = &afs_extend_lock_operation; + return afs_do_sync_operation(op); } +static const struct afs_operation_ops afs_release_lock_operation = { + .issue_afs_rpc = afs_fs_release_lock, + .issue_yfs_rpc = yfs_fs_release_lock, + .success = afs_lock_success, +}; + /* * Release a lock on a file */ static int afs_release_lock(struct afs_vnode *vnode, struct key *key) { - struct afs_status_cb *scb; - struct afs_operation fc; - int ret; + struct afs_operation *op; _enter("%s{%llx:%llu.%u},%x", vnode->volume->name, @@ -263,25 +265,15 @@ 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; + op = afs_alloc_operation(key, vnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - 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, scb); - } + afs_op_set_vnode(op, 0, vnode); - 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; + op->flags |= AFS_OPERATION_UNINTR; + op->ops = &afs_release_lock_operation; + return afs_do_sync_operation(op); } /* diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c new file mode 100644 index 000000000000..f7a768d12141 --- /dev/null +++ b/fs/afs/fs_operation.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Fileserver-directed operation handling. + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include "internal.h" + +static atomic_t afs_operation_debug_counter; + +/* + * Create an operation against a volume. + */ +struct afs_operation *afs_alloc_operation(struct key *key, struct afs_volume *volume) +{ + struct afs_operation *op; + + _enter(""); + + op = kzalloc(sizeof(*op), GFP_KERNEL); + if (!op) + return ERR_PTR(-ENOMEM); + + if (!key) { + key = afs_request_key(volume->cell); + if (IS_ERR(key)) { + kfree(op); + return ERR_CAST(key); + } + } else { + key_get(key); + } + + op->key = key; + op->volume = afs_get_volume(volume); + op->net = volume->cell->net; + op->cb_v_break = volume->cb_v_break; + op->debug_id = atomic_inc_return(&afs_operation_debug_counter); + op->error = -EDESTADDRREQ; + op->ac.error = SHRT_MAX; + + _leave(" = [op=%08x]", op->debug_id); + return op; +} + +/* + * Lock the vnode(s) being operated upon. + */ +static bool afs_get_io_locks(struct afs_operation *op) +{ + struct afs_vnode *vnode = op->file[0].vnode; + struct afs_vnode *vnode2 = op->file[1].vnode; + + _enter(""); + + if (op->flags & AFS_OPERATION_UNINTR) { + mutex_lock(&vnode->io_lock); + op->flags |= AFS_OPERATION_LOCK_0; + _leave(" = t [1]"); + return true; + } + + if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2) + vnode2 = NULL; + + if (vnode2 > vnode) + swap(vnode, vnode2); + + if (mutex_lock_interruptible(&vnode->io_lock) < 0) { + op->error = -EINTR; + op->flags |= AFS_OPERATION_STOP; + _leave(" = f [I 0]"); + return false; + } + op->flags |= AFS_OPERATION_LOCK_0; + + if (vnode2) { + if (mutex_lock_interruptible_nested(&vnode2->io_lock, 1) < 0) { + op->error = -EINTR; + op->flags |= AFS_OPERATION_STOP; + mutex_unlock(&vnode->io_lock); + op->flags &= ~AFS_OPERATION_LOCK_0; + _leave(" = f [I 1]"); + return false; + } + op->flags |= AFS_OPERATION_LOCK_1; + } + + _leave(" = t [2]"); + return true; +} + +static void afs_drop_io_locks(struct afs_operation *op) +{ + struct afs_vnode *vnode = op->file[0].vnode; + struct afs_vnode *vnode2 = op->file[1].vnode; + + _enter(""); + + if (op->flags & AFS_OPERATION_LOCK_1) + mutex_unlock(&vnode2->io_lock); + if (op->flags & AFS_OPERATION_LOCK_0) + mutex_unlock(&vnode->io_lock); +} + +static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp, + unsigned int index) +{ + struct afs_vnode *vnode = vp->vnode; + + if (vnode) { + vp->fid = vnode->fid; + vp->dv_before = vnode->status.data_version; + vp->cb_break_before = afs_calc_vnode_cb_break(vnode); + if (vnode->lock_state != AFS_VNODE_LOCK_NONE) + op->flags |= AFS_OPERATION_CUR_ONLY; + } + + if (vp->fid.vnode) + _debug("PREP[%u] {%llx:%llu.%u}", + index, vp->fid.vid, vp->fid.vnode, vp->fid.unique); +} + +/* + * Begin an operation on the fileserver. + * + * Fileserver operations are serialised on the server by vnode, so we serialise + * them here also using the io_lock. + */ +bool afs_begin_vnode_operation(struct afs_operation *op) +{ + struct afs_vnode *vnode = op->file[0].vnode; + + ASSERT(vnode); + + _enter(""); + + if (op->file[0].need_io_lock) + if (!afs_get_io_locks(op)) + return false; + + read_seqlock_excl(&vnode->cb_lock); + op->cbi = afs_get_cb_interest( + rcu_dereference_protected(vnode->cb_interest, + lockdep_is_held(&vnode->cb_lock.lock))); + read_sequnlock_excl(&vnode->cb_lock); + + afs_prepare_vnode(op, &op->file[0], 0); + afs_prepare_vnode(op, &op->file[1], 1); + op->cb_v_break = op->volume->cb_v_break; + _leave(" = true"); + return true; +} + +/* + * Tidy up a filesystem cursor and unlock the vnode. + */ +static void afs_end_vnode_operation(struct afs_operation *op) +{ + _enter(""); + + if (op->error == -EDESTADDRREQ || + op->error == -EADDRNOTAVAIL || + op->error == -ENETUNREACH || + op->error == -EHOSTUNREACH) + afs_dump_edestaddrreq(op); + + afs_drop_io_locks(op); + + if (op->error == -ECONNABORTED) + op->error = afs_abort_to_error(op->ac.abort_code); +} + +/* + * Wait for an in-progress operation to complete. + */ +void afs_wait_for_operation(struct afs_operation *op) +{ + _enter(""); + + while (afs_select_fileserver(op)) { + op->cb_s_break = op->cbi->server->cb_s_break; + if (test_bit(AFS_SERVER_FL_IS_YFS, &op->cbi->server->flags) && + op->ops->issue_yfs_rpc) + op->ops->issue_yfs_rpc(op); + else + op->ops->issue_afs_rpc(op); + + op->error = afs_wait_for_call_to_complete(op->call, &op->ac); + } + + if (op->error == 0) { + _debug("success"); + op->ops->success(op); + } + + afs_end_vnode_operation(op); + + if (op->error == 0 && op->ops->edit_dir) { + _debug("edit_dir"); + op->ops->edit_dir(op); + } + _leave(""); +} + +/* + * Dispose of an operation. + */ +int afs_put_operation(struct afs_operation *op) +{ + int i, ret = op->error; + + _enter("op=%08x,%d", op->debug_id, ret); + + if (op->ops && op->ops->put) + op->ops->put(op); + if (op->file[0].put_vnode) + iput(&op->file[0].vnode->vfs_inode); + if (op->file[1].put_vnode) + iput(&op->file[1].vnode->vfs_inode); + + if (op->more_files) { + for (i = 0; i < op->nr_files - 2; i++) + if (op->more_files[i].put_vnode) + iput(&op->more_files[i].vnode->vfs_inode); + kfree(op->more_files); + } + + afs_end_cursor(&op->ac); + afs_put_cb_interest(op->net, op->cbi); + afs_put_serverlist(op->net, op->server_list); + afs_put_volume(op->net, op->volume); + kfree(op); + return ret; +} + +int afs_do_sync_operation(struct afs_operation *op) +{ + afs_begin_vnode_operation(op); + afs_wait_for_operation(op); + return afs_put_operation(op); +} diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 3e423e9daa24..b1b45f10583d 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -13,12 +13,6 @@ #include "internal.h" #include "afs_fs.h" #include "xdr_fs.h" -#include "protocol_yfs.h" - -static inline void afs_use_fs_server(struct afs_call *call, struct afs_cb_interest *cbi) -{ - call->cbi = afs_get_cb_interest(cbi); -} /* * decode an AFSFid block @@ -240,8 +234,10 @@ static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp, /* * deliver reply data to an FS.FetchStatus */ -static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call) +static int afs_deliver_fs_fetch_status(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *vp = &op->file[op->fetch_status.which]; const __be32 *bp; int ret; @@ -251,9 +247,9 @@ static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSCallBack(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_AFSCallBack(&bp, call, &vp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -262,54 +258,39 @@ static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call) /* * FS.FetchStatus operation type */ -static const struct afs_call_type afs_RXFSFetchStatus_vnode = { - .name = "FS.FetchStatus(vnode)", +static const struct afs_call_type afs_RXFSFetchStatus = { + .name = "FS.FetchStatus", .op = afs_FS_FetchStatus, - .deliver = afs_deliver_fs_fetch_status_vnode, + .deliver = afs_deliver_fs_fetch_status, .destructor = afs_flat_call_destructor, }; /* * fetch the status information for a file */ -int afs_fs_fetch_file_status(struct afs_operation *fc, struct afs_status_cb *scb, - struct afs_volsync *volsync) +void afs_fs_fetch_status(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[op->fetch_status.which]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_fetch_file_status(fc, scb, volsync); - _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus_vnode, + call = afs_alloc_flat_call(op->net, &afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4); - if (!call) { - fc->ac.error = -ENOMEM; - return -ENOMEM; - } - - call->key = fc->key; - call->out_scb = scb; - call->out_volsync = volsync; + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHSTATUS); - bp[1] = htonl(vnode->fid.vid); - bp[2] = htonl(vnode->fid.vnode); - bp[3] = htonl(vnode->fid.unique); + bp[1] = htonl(vp->fid.vid); + bp[2] = htonl(vp->fid.vnode); + bp[3] = htonl(vp->fid.unique); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -317,7 +298,9 @@ int afs_fs_fetch_file_status(struct afs_operation *fc, struct afs_status_cb *scb */ static int afs_deliver_fs_fetch_data(struct afs_call *call) { - struct afs_read *req = call->read_request; + struct afs_operation *op = call->op; + struct afs_vnode_param *vp = &op->file[0]; + struct afs_read *req = op->fetch.req; const __be32 *bp; unsigned int size; int ret; @@ -414,12 +397,12 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSCallBack(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_AFSCallBack(&bp, call, &vp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); - req->data_version = call->out_scb->status.data_version; - req->file_size = call->out_scb->status.size; + req->data_version = vp->scb.status.data_version; + req->file_size = vp->scb.status.size; call->unmarshall++; @@ -442,14 +425,6 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) return 0; } -static void afs_fetch_data_destructor(struct afs_call *call) -{ - struct afs_read *req = call->read_request; - - afs_put_read(req); - afs_flat_call_destructor(call); -} - /* * FS.FetchData operation type */ @@ -457,102 +432,79 @@ static const struct afs_call_type afs_RXFSFetchData = { .name = "FS.FetchData", .op = afs_FS_FetchData, .deliver = afs_deliver_fs_fetch_data, - .destructor = afs_fetch_data_destructor, + .destructor = afs_flat_call_destructor, }; static const struct afs_call_type afs_RXFSFetchData64 = { .name = "FS.FetchData64", .op = afs_FS_FetchData64, .deliver = afs_deliver_fs_fetch_data, - .destructor = afs_fetch_data_destructor, + .destructor = afs_flat_call_destructor, }; /* * fetch data from a very large file */ -static int afs_fs_fetch_data64(struct afs_operation *fc, - struct afs_status_cb *scb, - struct afs_read *req) +static void afs_fs_fetch_data64(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; + struct afs_read *req = op->fetch.req; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(""); - call = afs_alloc_flat_call(net, &afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_scb = scb; - call->out_volsync = NULL; - call->read_request = afs_get_read(req); + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHDATA64); - bp[1] = htonl(vnode->fid.vid); - bp[2] = htonl(vnode->fid.vnode); - bp[3] = htonl(vnode->fid.unique); + bp[1] = htonl(vp->fid.vid); + bp[2] = htonl(vp->fid.vnode); + bp[3] = htonl(vp->fid.unique); bp[4] = htonl(upper_32_bits(req->pos)); bp[5] = htonl(lower_32_bits(req->pos)); bp[6] = 0; bp[7] = htonl(lower_32_bits(req->len)); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* * fetch data from a file */ -int afs_fs_fetch_data(struct afs_operation *fc, - struct afs_status_cb *scb, - struct afs_read *req) +void afs_fs_fetch_data(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); + struct afs_read *req = op->fetch.req; __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_fetch_data(fc, scb, req); - if (upper_32_bits(req->pos) || upper_32_bits(req->len) || upper_32_bits(req->pos + req->len)) - return afs_fs_fetch_data64(fc, scb, req); + return afs_fs_fetch_data64(op); _enter(""); - call = afs_alloc_flat_call(net, &afs_RXFSFetchData, 24, (21 + 3 + 6) * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSFetchData, 24, (21 + 3 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_scb = scb; - call->out_volsync = NULL; - call->read_request = afs_get_read(req); + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHDATA); - bp[1] = htonl(vnode->fid.vid); - bp[2] = htonl(vnode->fid.vnode); - bp[3] = htonl(vnode->fid.unique); + bp[1] = htonl(vp->fid.vid); + bp[2] = htonl(vp->fid.vnode); + bp[3] = htonl(vp->fid.unique); bp[4] = htonl(lower_32_bits(req->pos)); bp[5] = htonl(lower_32_bits(req->len)); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -560,6 +512,9 @@ int afs_fs_fetch_data(struct afs_operation *fc, */ static int afs_deliver_fs_create_vnode(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; const __be32 *bp; int ret; @@ -569,11 +524,11 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFid(&bp, call->out_fid); - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_AFSCallBack(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFid(&bp, &op->file[1].fid); + xdr_decode_AFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_AFSFetchStatus(&bp, call, &dvp->scb); + xdr_decode_AFSCallBack(&bp, call, &vp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -589,6 +544,52 @@ static const struct afs_call_type afs_RXFSCreateFile = { .destructor = afs_flat_call_destructor, }; +/* + * Create a file. + */ +void afs_fs_create_file(struct afs_operation *op) +{ + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_call *call; + size_t namesz, reqsz, padsz; + __be32 *bp; + + _enter(""); + + namesz = name->len; + padsz = (4 - (namesz & 3)) & 3; + reqsz = (5 * 4) + namesz + padsz + (6 * 4); + + call = afs_alloc_flat_call(op->net, &afs_RXFSCreateFile, + reqsz, (3 + 21 + 21 + 3 + 6) * 4); + if (!call) + return afs_op_nomem(op); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSCREATEFILE); + *bp++ = htonl(dvp->fid.vid); + *bp++ = htonl(dvp->fid.vnode); + *bp++ = htonl(dvp->fid.unique); + *bp++ = htonl(namesz); + memcpy(bp, name->name, namesz); + bp = (void *) bp + namesz; + if (padsz > 0) { + memset(bp, 0, padsz); + bp = (void *) bp + padsz; + } + *bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME); + *bp++ = htonl(op->mtime.tv_sec); /* mtime */ + *bp++ = 0; /* owner */ + *bp++ = 0; /* group */ + *bp++ = htonl(op->create.mode & S_IALLUGO); /* unix mode */ + *bp++ = 0; /* segment size */ + + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); +} + static const struct afs_call_type afs_RXFSMakeDir = { .name = "FS.MakeDir", .op = afs_FS_MakeDir, @@ -597,80 +598,58 @@ static const struct afs_call_type afs_RXFSMakeDir = { }; /* - * create a file or make a directory + * Create a new directory */ -int afs_fs_create(struct afs_operation *fc, - const char *name, - umode_t mode, - struct afs_status_cb *dvnode_scb, - struct afs_fid *newfid, - struct afs_status_cb *new_scb) +void afs_fs_make_dir(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(dvnode); size_t namesz, reqsz, padsz; __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)){ - if (S_ISDIR(mode)) - return yfs_fs_make_dir(fc, name, mode, dvnode_scb, - newfid, new_scb); - else - return yfs_fs_create_file(fc, name, mode, dvnode_scb, - newfid, new_scb); - } - _enter(""); - namesz = strlen(name); + namesz = name->len; padsz = (4 - (namesz & 3)) & 3; reqsz = (5 * 4) + namesz + padsz + (6 * 4); - call = afs_alloc_flat_call( - net, S_ISDIR(mode) ? &afs_RXFSMakeDir : &afs_RXFSCreateFile, - reqsz, (3 + 21 + 21 + 3 + 6) * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSMakeDir, + reqsz, (3 + 21 + 21 + 3 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; - call->out_fid = newfid; - call->out_scb = new_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; - *bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE); - *bp++ = htonl(dvnode->fid.vid); - *bp++ = htonl(dvnode->fid.vnode); - *bp++ = htonl(dvnode->fid.unique); + *bp++ = htonl(FSMAKEDIR); + *bp++ = htonl(dvp->fid.vid); + *bp++ = htonl(dvp->fid.vnode); + *bp++ = htonl(dvp->fid.unique); *bp++ = htonl(namesz); - memcpy(bp, name, namesz); + memcpy(bp, name->name, namesz); bp = (void *) bp + namesz; if (padsz > 0) { memset(bp, 0, padsz); bp = (void *) bp + padsz; } *bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME); - *bp++ = htonl(dvnode->vfs_inode.i_mtime.tv_sec); /* mtime */ + *bp++ = htonl(op->mtime.tv_sec); /* mtime */ *bp++ = 0; /* owner */ *bp++ = 0; /* group */ - *bp++ = htonl(mode & S_IALLUGO); /* unix mode */ + *bp++ = htonl(op->create.mode & S_IALLUGO); /* unix mode */ *bp++ = 0; /* segment size */ - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &dvnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* - * Deliver reply data to any operation that returns directory status and volume - * sync. + * Deliver reply data to any operation that returns status and volume sync. */ -static int afs_deliver_fs_dir_status_and_vol(struct afs_call *call) +static int afs_deliver_fs_file_status_and_vol(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *vp = &op->file[0]; const __be32 *bp; int ret; @@ -680,79 +659,108 @@ static int afs_deliver_fs_dir_status_and_vol(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; } /* - * FS.RemoveDir/FS.RemoveFile operation type + * FS.RemoveFile operation type */ static const struct afs_call_type afs_RXFSRemoveFile = { .name = "FS.RemoveFile", .op = afs_FS_RemoveFile, - .deliver = afs_deliver_fs_dir_status_and_vol, + .deliver = afs_deliver_fs_file_status_and_vol, .destructor = afs_flat_call_destructor, }; +/* + * Remove a file. + */ +void afs_fs_remove_file(struct afs_operation *op) +{ + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_call *call; + size_t namesz, reqsz, padsz; + __be32 *bp; + + _enter(""); + + namesz = name->len; + padsz = (4 - (namesz & 3)) & 3; + reqsz = (5 * 4) + namesz + padsz; + + call = afs_alloc_flat_call(op->net, &afs_RXFSRemoveFile, + reqsz, (21 + 6) * 4); + if (!call) + return afs_op_nomem(op); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSREMOVEFILE); + *bp++ = htonl(dvp->fid.vid); + *bp++ = htonl(dvp->fid.vnode); + *bp++ = htonl(dvp->fid.unique); + *bp++ = htonl(namesz); + memcpy(bp, name->name, namesz); + bp = (void *) bp + namesz; + if (padsz > 0) { + memset(bp, 0, padsz); + bp = (void *) bp + padsz; + } + + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); +} + static const struct afs_call_type afs_RXFSRemoveDir = { .name = "FS.RemoveDir", .op = afs_FS_RemoveDir, - .deliver = afs_deliver_fs_dir_status_and_vol, + .deliver = afs_deliver_fs_file_status_and_vol, .destructor = afs_flat_call_destructor, }; /* - * remove a file or directory + * Remove a directory. */ -int afs_fs_remove(struct afs_operation *fc, struct afs_vnode *vnode, - const char *name, bool isdir, struct afs_status_cb *dvnode_scb) +void afs_fs_remove_dir(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(dvnode); size_t namesz, reqsz, padsz; __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_remove(fc, vnode, name, isdir, dvnode_scb); - _enter(""); - namesz = strlen(name); + namesz = name->len; padsz = (4 - (namesz & 3)) & 3; reqsz = (5 * 4) + namesz + padsz; - call = afs_alloc_flat_call( - net, isdir ? &afs_RXFSRemoveDir : &afs_RXFSRemoveFile, - reqsz, (21 + 6) * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSRemoveDir, + reqsz, (21 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; - *bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE); - *bp++ = htonl(dvnode->fid.vid); - *bp++ = htonl(dvnode->fid.vnode); - *bp++ = htonl(dvnode->fid.unique); + *bp++ = htonl(FSREMOVEDIR); + *bp++ = htonl(dvp->fid.vid); + *bp++ = htonl(dvp->fid.vnode); + *bp++ = htonl(dvp->fid.unique); *bp++ = htonl(namesz); - memcpy(bp, name, namesz); + memcpy(bp, name->name, namesz); bp = (void *) bp + namesz; if (padsz > 0) { memset(bp, 0, padsz); bp = (void *) bp + padsz; } - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &dvnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -760,6 +768,9 @@ int afs_fs_remove(struct afs_operation *fc, struct afs_vnode *vnode, */ static int afs_deliver_fs_link(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; const __be32 *bp; int ret; @@ -771,9 +782,9 @@ static int afs_deliver_fs_link(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_AFSFetchStatus(&bp, call, &dvp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -792,56 +803,44 @@ static const struct afs_call_type afs_RXFSLink = { /* * make a hard link */ -int afs_fs_link(struct afs_operation *fc, struct afs_vnode *vnode, - const char *name, - struct afs_status_cb *dvnode_scb, - struct afs_status_cb *vnode_scb) +void afs_fs_link(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); size_t namesz, reqsz, padsz; __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_link(fc, vnode, name, dvnode_scb, vnode_scb); - _enter(""); - namesz = strlen(name); + namesz = name->len; padsz = (4 - (namesz & 3)) & 3; reqsz = (5 * 4) + namesz + padsz + (3 * 4); - call = afs_alloc_flat_call(net, &afs_RXFSLink, reqsz, (21 + 21 + 6) * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSLink, reqsz, (21 + 21 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; - call->out_scb = vnode_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSLINK); - *bp++ = htonl(dvnode->fid.vid); - *bp++ = htonl(dvnode->fid.vnode); - *bp++ = htonl(dvnode->fid.unique); + *bp++ = htonl(dvp->fid.vid); + *bp++ = htonl(dvp->fid.vnode); + *bp++ = htonl(dvp->fid.unique); *bp++ = htonl(namesz); - memcpy(bp, name, namesz); + memcpy(bp, name->name, namesz); bp = (void *) bp + namesz; if (padsz > 0) { memset(bp, 0, padsz); bp = (void *) bp + padsz; } - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); - - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &vnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); + + trace_afs_make_fs_call1(call, &vp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -849,6 +848,9 @@ int afs_fs_link(struct afs_operation *fc, struct afs_vnode *vnode, */ static int afs_deliver_fs_symlink(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; const __be32 *bp; int ret; @@ -860,10 +862,10 @@ static int afs_deliver_fs_symlink(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFid(&bp, call->out_fid); - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFid(&bp, &vp->fid); + xdr_decode_AFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_AFSFetchStatus(&bp, call, &dvp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -882,75 +884,58 @@ static const struct afs_call_type afs_RXFSSymlink = { /* * create a symbolic link */ -int afs_fs_symlink(struct afs_operation *fc, - const char *name, - const char *contents, - struct afs_status_cb *dvnode_scb, - struct afs_fid *newfid, - struct afs_status_cb *new_scb) +void afs_fs_symlink(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(dvnode); size_t namesz, reqsz, padsz, c_namesz, c_padsz; __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_symlink(fc, name, contents, dvnode_scb, - newfid, new_scb); - _enter(""); - namesz = strlen(name); + namesz = name->len; padsz = (4 - (namesz & 3)) & 3; - c_namesz = strlen(contents); + c_namesz = strlen(op->create.symlink); c_padsz = (4 - (c_namesz & 3)) & 3; reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4); - call = afs_alloc_flat_call(net, &afs_RXFSSymlink, reqsz, + call = afs_alloc_flat_call(op->net, &afs_RXFSSymlink, reqsz, (3 + 21 + 21 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; - call->out_fid = newfid; - call->out_scb = new_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSSYMLINK); - *bp++ = htonl(dvnode->fid.vid); - *bp++ = htonl(dvnode->fid.vnode); - *bp++ = htonl(dvnode->fid.unique); + *bp++ = htonl(dvp->fid.vid); + *bp++ = htonl(dvp->fid.vnode); + *bp++ = htonl(dvp->fid.unique); *bp++ = htonl(namesz); - memcpy(bp, name, namesz); + memcpy(bp, name->name, namesz); bp = (void *) bp + namesz; if (padsz > 0) { memset(bp, 0, padsz); bp = (void *) bp + padsz; } *bp++ = htonl(c_namesz); - memcpy(bp, contents, c_namesz); + memcpy(bp, op->create.symlink, c_namesz); bp = (void *) bp + c_namesz; if (c_padsz > 0) { memset(bp, 0, c_padsz); bp = (void *) bp + c_padsz; } *bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME); - *bp++ = htonl(dvnode->vfs_inode.i_mtime.tv_sec); /* mtime */ + *bp++ = htonl(op->mtime.tv_sec); /* mtime */ *bp++ = 0; /* owner */ *bp++ = 0; /* group */ *bp++ = htonl(S_IRWXUGO); /* unix mode */ *bp++ = 0; /* segment size */ - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &dvnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -958,6 +943,9 @@ int afs_fs_symlink(struct afs_operation *fc, */ static int afs_deliver_fs_rename(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *orig_dvp = &op->file[0]; + struct afs_vnode_param *new_dvp = &op->file[1]; const __be32 *bp; int ret; @@ -969,9 +957,9 @@ static int afs_deliver_fs_rename(struct afs_call *call) /* If the two dirs are the same, we have two copies of the same status * report, so we just decode it twice. */ - xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFetchStatus(&bp, call, &orig_dvp->scb); + xdr_decode_AFSFetchStatus(&bp, call, &new_dvp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -990,31 +978,22 @@ static const struct afs_call_type afs_RXFSRename = { /* * Rename/move a file or directory. */ -int afs_fs_rename(struct afs_operation *fc, - const char *orig_name, - struct afs_vnode *new_dvnode, - const char *new_name, - struct afs_status_cb *orig_dvnode_scb, - struct afs_status_cb *new_dvnode_scb) +void afs_fs_rename(struct afs_operation *op) { - struct afs_vnode *orig_dvnode = fc->vnode; + struct afs_vnode_param *orig_dvp = &op->file[0]; + struct afs_vnode_param *new_dvp = &op->file[1]; + const struct qstr *orig_name = &op->dentry->d_name; + const struct qstr *new_name = &op->dentry_2->d_name; struct afs_call *call; - struct afs_net *net = afs_v2net(orig_dvnode); size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz; __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_rename(fc, orig_name, - new_dvnode, new_name, - orig_dvnode_scb, - new_dvnode_scb); - _enter(""); - o_namesz = strlen(orig_name); + o_namesz = orig_name->len; o_padsz = (4 - (o_namesz & 3)) & 3; - n_namesz = strlen(new_name); + n_namesz = new_name->len; n_padsz = (4 - (n_namesz & 3)) & 3; reqsz = (4 * 4) + @@ -1022,51 +1001,46 @@ int afs_fs_rename(struct afs_operation *fc, (3 * 4) + 4 + n_namesz + n_padsz; - call = afs_alloc_flat_call(net, &afs_RXFSRename, reqsz, (21 + 21 + 6) * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSRename, reqsz, (21 + 21 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = orig_dvnode_scb; - call->out_scb = new_dvnode_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSRENAME); - *bp++ = htonl(orig_dvnode->fid.vid); - *bp++ = htonl(orig_dvnode->fid.vnode); - *bp++ = htonl(orig_dvnode->fid.unique); + *bp++ = htonl(orig_dvp->fid.vid); + *bp++ = htonl(orig_dvp->fid.vnode); + *bp++ = htonl(orig_dvp->fid.unique); *bp++ = htonl(o_namesz); - memcpy(bp, orig_name, o_namesz); + memcpy(bp, orig_name->name, o_namesz); bp = (void *) bp + o_namesz; if (o_padsz > 0) { memset(bp, 0, o_padsz); bp = (void *) bp + o_padsz; } - *bp++ = htonl(new_dvnode->fid.vid); - *bp++ = htonl(new_dvnode->fid.vnode); - *bp++ = htonl(new_dvnode->fid.unique); + *bp++ = htonl(new_dvp->fid.vid); + *bp++ = htonl(new_dvp->fid.vnode); + *bp++ = htonl(new_dvp->fid.unique); *bp++ = htonl(n_namesz); - memcpy(bp, new_name, n_namesz); + memcpy(bp, new_name->name, n_namesz); bp = (void *) bp + n_namesz; if (n_padsz > 0) { memset(bp, 0, n_padsz); bp = (void *) bp + n_padsz; } - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call2(call, &orig_dvnode->fid, orig_name, new_name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call2(call, &orig_dvp->fid, orig_name, new_name); + afs_make_op_call(op, call, GFP_NOFS); } /* - * deliver reply data to an FS.StoreData + * Deliver reply data to FS.StoreData or FS.StoreStatus */ static int afs_deliver_fs_store_data(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *vp = &op->file[0]; const __be32 *bp; int ret; @@ -1078,8 +1052,8 @@ static int afs_deliver_fs_store_data(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -1105,90 +1079,69 @@ static const struct afs_call_type afs_RXFSStoreData64 = { /* * store a set of pages to a very large file */ -static int afs_fs_store_data64(struct afs_operation *fc, - struct address_space *mapping, - pgoff_t first, pgoff_t last, - unsigned offset, unsigned to, - loff_t size, loff_t pos, loff_t i_size, - struct afs_status_cb *scb) +static void afs_fs_store_data64(struct afs_operation *op, + loff_t pos, loff_t size, loff_t i_size) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call = afs_alloc_flat_call(net, &afs_RXFSStoreData64, + call = afs_alloc_flat_call(op->net, &afs_RXFSStoreData64, (4 + 6 + 3 * 2) * 4, (21 + 6) * 4); if (!call) - return -ENOMEM; + return afs_op_nomem(op); - call->key = fc->key; - call->mapping = mapping; - call->first = first; - call->last = last; - call->first_offset = offset; - call->last_to = to; call->send_pages = true; - call->out_scb = scb; /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSSTOREDATA64); - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); *bp++ = htonl(AFS_SET_MTIME); /* mask */ - *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ + *bp++ = htonl(op->mtime.tv_sec); /* mtime */ *bp++ = 0; /* owner */ *bp++ = 0; /* group */ *bp++ = 0; /* unix mode */ *bp++ = 0; /* segment size */ - *bp++ = htonl(pos >> 32); - *bp++ = htonl((u32) pos); - *bp++ = htonl(size >> 32); - *bp++ = htonl((u32) size); - *bp++ = htonl(i_size >> 32); - *bp++ = htonl((u32) i_size); - - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + *bp++ = htonl(upper_32_bits(pos)); + *bp++ = htonl(lower_32_bits(pos)); + *bp++ = htonl(upper_32_bits(size)); + *bp++ = htonl(lower_32_bits(size)); + *bp++ = htonl(upper_32_bits(i_size)); + *bp++ = htonl(lower_32_bits(i_size)); + + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* * store a set of pages */ -int afs_fs_store_data(struct afs_operation *fc, struct address_space *mapping, - pgoff_t first, pgoff_t last, - unsigned offset, unsigned to, - struct afs_status_cb *scb) +void afs_fs_store_data(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); loff_t size, pos, i_size; __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_store_data(fc, mapping, first, last, offset, to, scb); - _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - size = (loff_t)to - (loff_t)offset; - if (first != last) - size += (loff_t)(last - first) << PAGE_SHIFT; - pos = (loff_t)first << PAGE_SHIFT; - pos += offset; + size = (loff_t)op->store.last_to - (loff_t)op->store.first_offset; + if (op->store.first != op->store.last) + size += (loff_t)(op->store.last - op->store.first) << PAGE_SHIFT; + pos = (loff_t)op->store.first << PAGE_SHIFT; + pos += op->store.first_offset; - i_size = i_size_read(&vnode->vfs_inode); + i_size = i_size_read(&vp->vnode->vfs_inode); if (pos + size > i_size) i_size = size + pos; @@ -1196,71 +1149,38 @@ int afs_fs_store_data(struct afs_operation *fc, struct address_space *mapping, (unsigned long long) size, (unsigned long long) pos, (unsigned long long) i_size); - if (pos >> 32 || i_size >> 32 || size >> 32 || (pos + size) >> 32) - return afs_fs_store_data64(fc, mapping, first, last, offset, to, - size, pos, i_size, scb); + if (upper_32_bits(pos) || upper_32_bits(i_size) || upper_32_bits(size) || + upper_32_bits(pos + size)) + return afs_fs_store_data64(op, pos, size, i_size); - call = afs_alloc_flat_call(net, &afs_RXFSStoreData, + call = afs_alloc_flat_call(op->net, &afs_RXFSStoreData, (4 + 6 + 3) * 4, (21 + 6) * 4); if (!call) - return -ENOMEM; + return afs_op_nomem(op); - call->key = fc->key; - call->mapping = mapping; - call->first = first; - call->last = last; - call->first_offset = offset; - call->last_to = to; call->send_pages = true; - call->out_scb = scb; /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSSTOREDATA); - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); *bp++ = htonl(AFS_SET_MTIME); /* mask */ - *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ + *bp++ = htonl(op->mtime.tv_sec); /* mtime */ *bp++ = 0; /* owner */ *bp++ = 0; /* group */ *bp++ = 0; /* unix mode */ *bp++ = 0; /* segment size */ - *bp++ = htonl(pos); - *bp++ = htonl(size); - *bp++ = htonl(i_size); + *bp++ = htonl(lower_32_bits(pos)); + *bp++ = htonl(lower_32_bits(size)); + *bp++ = htonl(lower_32_bits(i_size)); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); -} - -/* - * deliver reply data to an FS.StoreStatus - */ -static int afs_deliver_fs_store_status(struct afs_call *call) -{ - const __be32 *bp; - int ret; - - _enter(""); - - ret = afs_transfer_reply(call); - if (ret < 0) - return ret; - - /* unmarshall the reply once we've received all of it */ - bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); - - _leave(" = 0 [done]"); - return 0; + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1269,21 +1189,21 @@ static int afs_deliver_fs_store_status(struct afs_call *call) static const struct afs_call_type afs_RXFSStoreStatus = { .name = "FS.StoreStatus", .op = afs_FS_StoreStatus, - .deliver = afs_deliver_fs_store_status, + .deliver = afs_deliver_fs_store_data, .destructor = afs_flat_call_destructor, }; static const struct afs_call_type afs_RXFSStoreData_as_Status = { .name = "FS.StoreData", .op = afs_FS_StoreData, - .deliver = afs_deliver_fs_store_status, + .deliver = afs_deliver_fs_store_data, .destructor = afs_flat_call_destructor, }; static const struct afs_call_type afs_RXFSStoreData64_as_Status = { .name = "FS.StoreData64", .op = afs_FS_StoreData64, - .deliver = afs_deliver_fs_store_status, + .deliver = afs_deliver_fs_store_data, .destructor = afs_flat_call_destructor, }; @@ -1291,85 +1211,74 @@ static const struct afs_call_type afs_RXFSStoreData64_as_Status = { * set the attributes on a very large file, using FS.StoreData rather than * FS.StoreStatus so as to alter the file size also */ -static int afs_fs_setattr_size64(struct afs_operation *fc, struct iattr *attr, - struct afs_status_cb *scb) +static void afs_fs_setattr_size64(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); + struct iattr *attr = op->setattr.attr; __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); ASSERT(attr->ia_valid & ATTR_SIZE); - call = afs_alloc_flat_call(net, &afs_RXFSStoreData64_as_Status, + call = afs_alloc_flat_call(op->net, &afs_RXFSStoreData64_as_Status, (4 + 6 + 3 * 2) * 4, (21 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSSTOREDATA64); - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); xdr_encode_AFS_StoreStatus(&bp, attr); - *bp++ = htonl(attr->ia_size >> 32); /* position of start of write */ - *bp++ = htonl((u32) attr->ia_size); - *bp++ = 0; /* size of write */ + *bp++ = htonl(upper_32_bits(attr->ia_size)); /* position of start of write */ + *bp++ = htonl(lower_32_bits(attr->ia_size)); + *bp++ = 0; /* size of write */ *bp++ = 0; - *bp++ = htonl(attr->ia_size >> 32); /* new file length */ - *bp++ = htonl((u32) attr->ia_size); - - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + *bp++ = htonl(upper_32_bits(attr->ia_size)); /* new file length */ + *bp++ = htonl(lower_32_bits(attr->ia_size)); + + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* * set the attributes on a file, using FS.StoreData rather than FS.StoreStatus * so as to alter the file size also */ -static int afs_fs_setattr_size(struct afs_operation *fc, struct iattr *attr, - struct afs_status_cb *scb) +static void afs_fs_setattr_size(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); + struct iattr *attr = op->setattr.attr; __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); ASSERT(attr->ia_valid & ATTR_SIZE); - if (attr->ia_size >> 32) - return afs_fs_setattr_size64(fc, attr, scb); + if (upper_32_bits(attr->ia_size)) + return afs_fs_setattr_size64(op); - call = afs_alloc_flat_call(net, &afs_RXFSStoreData_as_Status, + call = afs_alloc_flat_call(op->net, &afs_RXFSStoreData_as_Status, (4 + 6 + 3) * 4, (21 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSSTOREDATA); - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); xdr_encode_AFS_StoreStatus(&bp, attr); @@ -1377,57 +1286,44 @@ static int afs_fs_setattr_size(struct afs_operation *fc, struct iattr *attr, *bp++ = 0; /* size of write */ *bp++ = htonl(attr->ia_size); /* new file length */ - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* * set the attributes on a file, using FS.StoreData if there's a change in file * size, and FS.StoreStatus otherwise */ -int afs_fs_setattr(struct afs_operation *fc, struct iattr *attr, - struct afs_status_cb *scb) +void afs_fs_setattr(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); + struct iattr *attr = op->setattr.attr; __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_setattr(fc, attr, scb); - if (attr->ia_valid & ATTR_SIZE) - return afs_fs_setattr_size(fc, attr, scb); + return afs_fs_setattr_size(op); _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call = afs_alloc_flat_call(net, &afs_RXFSStoreStatus, + call = afs_alloc_flat_call(op->net, &afs_RXFSStoreStatus, (4 + 6) * 4, (21 + 6) * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSSTORESTATUS); - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); - xdr_encode_AFS_StoreStatus(&bp, attr); + xdr_encode_AFS_StoreStatus(&bp, op->setattr.attr); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1435,6 +1331,7 @@ int afs_fs_setattr(struct afs_operation *fc, struct iattr *attr, */ static int afs_deliver_fs_get_volume_status(struct afs_call *call) { + struct afs_operation *op = call->op; const __be32 *bp; char *p; u32 size; @@ -1456,7 +1353,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_AFSFetchVolumeStatus(&bp, call->out_volstatus); + xdr_decode_AFSFetchVolumeStatus(&bp, &op->volstatus.vs); call->unmarshall++; afs_extract_to_tmp(call); /* Fall through */ @@ -1569,37 +1466,26 @@ static const struct afs_call_type afs_RXFSGetVolumeStatus = { /* * fetch the status of a volume */ -int afs_fs_get_volume_status(struct afs_operation *fc, - struct afs_volume_status *vs) +void afs_fs_get_volume_status(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_get_volume_status(fc, vs); - _enter(""); - call = afs_alloc_flat_call(net, &afs_RXFSGetVolumeStatus, 2 * 4, + call = afs_alloc_flat_call(op->net, &afs_RXFSGetVolumeStatus, 2 * 4, max(12 * 4, AFSOPAQUEMAX + 1)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_volstatus = vs; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSGETVOLUMESTATUS); - bp[1] = htonl(vnode->fid.vid); + bp[1] = htonl(vp->fid.vid); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1607,6 +1493,7 @@ int afs_fs_get_volume_status(struct afs_operation *fc, */ static int afs_deliver_fs_xxxx_lock(struct afs_call *call) { + struct afs_operation *op = call->op; const __be32 *bp; int ret; @@ -1618,7 +1505,7 @@ static int afs_deliver_fs_xxxx_lock(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -1659,114 +1546,80 @@ static const struct afs_call_type afs_RXFSReleaseLock = { /* * Set a lock on a file */ -int afs_fs_set_lock(struct afs_operation *fc, afs_lock_type_t type, - struct afs_status_cb *scb) +void afs_fs_set_lock(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_set_lock(fc, type, scb); - _enter(""); - call = afs_alloc_flat_call(net, &afs_RXFSSetLock, 5 * 4, 6 * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSSetLock, 5 * 4, 6 * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->lvnode = vnode; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSSETLOCK); - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); - *bp++ = htonl(type); - - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_calli(call, &vnode->fid, type); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); + *bp++ = htonl(op->lock.type); + + trace_afs_make_fs_calli(call, &vp->fid, op->lock.type); + afs_make_op_call(op, call, GFP_NOFS); } /* * extend a lock on a file */ -int afs_fs_extend_lock(struct afs_operation *fc, struct afs_status_cb *scb) +void afs_fs_extend_lock(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_extend_lock(fc, scb); - _enter(""); - call = afs_alloc_flat_call(net, &afs_RXFSExtendLock, 4 * 4, 6 * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSExtendLock, 4 * 4, 6 * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->lvnode = vnode; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSEXTENDLOCK); - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); - - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); + + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* * release a lock on a file */ -int afs_fs_release_lock(struct afs_operation *fc, struct afs_status_cb *scb) +void afs_fs_release_lock(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_release_lock(fc, scb); - _enter(""); - call = afs_alloc_flat_call(net, &afs_RXFSReleaseLock, 4 * 4, 6 * 4); + call = afs_alloc_flat_call(op->net, &afs_RXFSReleaseLock, 4 * 4, 6 * 4); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->lvnode = vnode; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSRELEASELOCK); - *bp++ = htonl(vnode->fid.vid); - *bp++ = htonl(vnode->fid.vnode); - *bp++ = htonl(vnode->fid.unique); - - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); + + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1906,86 +1759,12 @@ bool afs_fs_get_capabilities(struct afs_net *net, struct afs_server *server, return true; } -/* - * Deliver reply data to an FS.FetchStatus with no vnode. - */ -static int afs_deliver_fs_fetch_status(struct afs_call *call) -{ - const __be32 *bp; - int ret; - - ret = afs_transfer_reply(call); - if (ret < 0) - return ret; - - /* unmarshall the reply once we've received all of it */ - bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSCallBack(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); - - _leave(" = 0 [done]"); - return 0; -} - -/* - * FS.FetchStatus operation type - */ -static const struct afs_call_type afs_RXFSFetchStatus = { - .name = "FS.FetchStatus", - .op = afs_FS_FetchStatus, - .deliver = afs_deliver_fs_fetch_status, - .destructor = afs_flat_call_destructor, -}; - -/* - * Fetch the status information for a fid without needing a vnode handle. - */ -int afs_fs_fetch_status(struct afs_operation *fc, - struct afs_net *net, - struct afs_fid *fid, - struct afs_status_cb *scb, - struct afs_volsync *volsync) -{ - struct afs_call *call; - __be32 *bp; - - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_fetch_status(fc, net, fid, scb, volsync); - - _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), fid->vid, fid->vnode); - - call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4); - if (!call) { - fc->ac.error = -ENOMEM; - return -ENOMEM; - } - - call->key = fc->key; - call->out_fid = fid; - call->out_scb = scb; - call->out_volsync = volsync; - - /* marshall the parameters */ - bp = call->request; - bp[0] = htonl(FSFETCHSTATUS); - bp[1] = htonl(fid->vid); - bp[2] = htonl(fid->vnode); - bp[3] = htonl(fid->unique); - - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); -} - /* * Deliver reply data to an FS.InlineBulkStatus call */ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) { + struct afs_operation *op = call->op; struct afs_status_cb *scb; const __be32 *bp; u32 tmp; @@ -2007,8 +1786,8 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) return ret; tmp = ntohl(call->tmp); - _debug("status count: %u/%u", tmp, call->count2); - if (tmp != call->count2) + _debug("status count: %u/%u", tmp, op->nr_files); + if (tmp != op->nr_files) return afs_protocol_error(call, afs_eproto_ibulkst_count); call->count = 0; @@ -2023,11 +1802,23 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) if (ret < 0) return ret; + switch (call->count) { + case 0: + scb = &op->file[0].scb; + break; + case 1: + scb = &op->file[1].scb; + break; + default: + scb = &op->more_files[call->count - 2].scb; + break; + } + bp = call->buffer; - scb = &call->out_scb[call->count]; xdr_decode_AFSFetchStatus(&bp, call, scb); + call->count++; - if (call->count < call->count2) + if (call->count < op->nr_files) goto more_counts; call->count = 0; @@ -2044,7 +1835,7 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) tmp = ntohl(call->tmp); _debug("CB count: %u", tmp); - if (tmp != call->count2) + if (tmp != op->nr_files) return afs_protocol_error(call, afs_eproto_ibulkst_cb_count); call->count = 0; call->unmarshall++; @@ -2059,11 +1850,22 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) return ret; _debug("unmarshall CB array"); + switch (call->count) { + case 0: + scb = &op->file[0].scb; + break; + case 1: + scb = &op->file[1].scb; + break; + default: + scb = &op->more_files[call->count - 2].scb; + break; + } + bp = call->buffer; - scb = &call->out_scb[call->count]; xdr_decode_AFSCallBack(&bp, call, scb); call->count++; - if (call->count < call->count2) + if (call->count < op->nr_files) goto more_cbs; afs_extract_to_buf(call, 6 * sizeof(__be32)); @@ -2076,7 +1878,7 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSVolSync(&bp, &op->volsync); call->unmarshall++; @@ -2088,6 +1890,13 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) return 0; } +static void afs_done_fs_inline_bulk_status(struct afs_call *call) +{ + if (call->error == -ECONNABORTED && + call->abort_code == RX_INVALID_OPERATION) + set_bit(AFS_SERVER_FL_NO_IBULK, &call->server->flags); +} + /* * FS.InlineBulkStatus operation type */ @@ -2095,58 +1904,53 @@ static const struct afs_call_type afs_RXFSInlineBulkStatus = { .name = "FS.InlineBulkStatus", .op = afs_FS_InlineBulkStatus, .deliver = afs_deliver_fs_inline_bulk_status, + .done = afs_done_fs_inline_bulk_status, .destructor = afs_flat_call_destructor, }; /* * Fetch the status information for up to 50 files */ -int afs_fs_inline_bulk_status(struct afs_operation *fc, - struct afs_net *net, - struct afs_fid *fids, - struct afs_status_cb *statuses, - unsigned int nr_fids, - struct afs_volsync *volsync) +void afs_fs_inline_bulk_status(struct afs_operation *op) { + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; struct afs_call *call; __be32 *bp; int i; - if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags)) - return yfs_fs_inline_bulk_status(fc, net, fids, statuses, - nr_fids, volsync); + if (test_bit(AFS_SERVER_FL_NO_IBULK, &op->cbi->server->flags)) { + op->error = -ENOTSUPP; + return; + } _enter(",%x,{%llx:%llu},%u", - key_serial(fc->key), fids[0].vid, fids[1].vnode, nr_fids); + key_serial(op->key), vp->fid.vid, vp->fid.vnode, op->nr_files); - call = afs_alloc_flat_call(net, &afs_RXFSInlineBulkStatus, - (2 + nr_fids * 3) * 4, + call = afs_alloc_flat_call(op->net, &afs_RXFSInlineBulkStatus, + (2 + op->nr_files * 3) * 4, 21 * 4); - if (!call) { - fc->ac.error = -ENOMEM; - return -ENOMEM; - } - - call->key = fc->key; - call->out_scb = statuses; - call->out_volsync = volsync; - call->count2 = nr_fids; + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; *bp++ = htonl(FSINLINEBULKSTATUS); - *bp++ = htonl(nr_fids); - for (i = 0; i < nr_fids; i++) { - *bp++ = htonl(fids[i].vid); - *bp++ = htonl(fids[i].vnode); - *bp++ = htonl(fids[i].unique); + *bp++ = htonl(op->nr_files); + *bp++ = htonl(dvp->fid.vid); + *bp++ = htonl(dvp->fid.vnode); + *bp++ = htonl(dvp->fid.unique); + *bp++ = htonl(vp->fid.vid); + *bp++ = htonl(vp->fid.vnode); + *bp++ = htonl(vp->fid.unique); + for (i = 0; i < op->nr_files - 2; i++) { + *bp++ = htonl(op->more_files[i].fid.vid); + *bp++ = htonl(op->more_files[i].fid.vnode); + *bp++ = htonl(op->more_files[i].fid.unique); } - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &fids[0]); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -2154,6 +1958,8 @@ int afs_fs_inline_bulk_status(struct afs_operation *fc, */ static int afs_deliver_fs_fetch_acl(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *vp = &op->file[0]; struct afs_acl *acl; const __be32 *bp; unsigned int size; @@ -2179,7 +1985,7 @@ static int afs_deliver_fs_fetch_acl(struct afs_call *call) acl = kmalloc(struct_size(acl, data, size), GFP_KERNEL); if (!acl) return -ENOMEM; - call->ret_acl = acl; + op->acl = acl; acl->size = call->count2; afs_extract_begin(call, acl->data, size); call->unmarshall++; @@ -2202,8 +2008,8 @@ static int afs_deliver_fs_fetch_acl(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); + xdr_decode_AFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_AFSVolSync(&bp, &op->volsync); call->unmarshall++; @@ -2215,12 +2021,6 @@ static int afs_deliver_fs_fetch_acl(struct afs_call *call) return 0; } -static void afs_destroy_fs_fetch_acl(struct afs_call *call) -{ - kfree(call->ret_acl); - afs_flat_call_destructor(call); -} - /* * FS.FetchACL operation type */ @@ -2228,66 +2028,33 @@ static const struct afs_call_type afs_RXFSFetchACL = { .name = "FS.FetchACL", .op = afs_FS_FetchACL, .deliver = afs_deliver_fs_fetch_acl, - .destructor = afs_destroy_fs_fetch_acl, }; /* * Fetch the ACL for a file. */ -struct afs_acl *afs_fs_fetch_acl(struct afs_operation *fc, - struct afs_status_cb *scb) +void afs_fs_fetch_acl(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); - - call = afs_alloc_flat_call(net, &afs_RXFSFetchACL, 16, (21 + 6) * 4); - if (!call) { - fc->ac.error = -ENOMEM; - return ERR_PTR(-ENOMEM); - } + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call->key = fc->key; - call->ret_acl = NULL; - call->out_scb = scb; - call->out_volsync = NULL; + call = afs_alloc_flat_call(op->net, &afs_RXFSFetchACL, 16, (21 + 6) * 4); + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHACL); - bp[1] = htonl(vnode->fid.vid); - bp[2] = htonl(vnode->fid.vnode); - bp[3] = htonl(vnode->fid.unique); - - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_make_call(&fc->ac, call, GFP_KERNEL); - return (struct afs_acl *)afs_wait_for_call_to_complete(call, &fc->ac); -} + bp[1] = htonl(vp->fid.vid); + bp[2] = htonl(vp->fid.vnode); + bp[3] = htonl(vp->fid.unique); -/* - * Deliver reply data to any operation that returns file status and volume - * sync. - */ -static int afs_deliver_fs_file_status_and_vol(struct afs_call *call) -{ - const __be32 *bp; - int ret; - - ret = afs_transfer_reply(call); - if (ret < 0) - return ret; - - bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_AFSVolSync(&bp, call->out_volsync); - - _leave(" = 0 [done]"); - return 0; + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_KERNEL); } /* @@ -2303,42 +2070,34 @@ static const struct afs_call_type afs_RXFSStoreACL = { /* * Fetch the ACL for a file. */ -int afs_fs_store_acl(struct afs_operation *fc, const struct afs_acl *acl, - struct afs_status_cb *scb) +void afs_fs_store_acl(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); + const struct afs_acl *acl = op->acl; size_t size; __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); size = round_up(acl->size, 4); - call = afs_alloc_flat_call(net, &afs_RXFSStoreACL, + call = afs_alloc_flat_call(op->net, &afs_RXFSStoreACL, 5 * 4 + size, (21 + 6) * 4); - if (!call) { - fc->ac.error = -ENOMEM; - return -ENOMEM; - } - - call->key = fc->key; - call->out_scb = scb; - call->out_volsync = NULL; + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSSTOREACL); - bp[1] = htonl(vnode->fid.vid); - bp[2] = htonl(vnode->fid.vnode); - bp[3] = htonl(vnode->fid.unique); + bp[1] = htonl(vp->fid.vid); + bp[2] = htonl(vp->fid.vnode); + bp[3] = htonl(vp->fid.unique); bp[4] = htonl(acl->size); memcpy(&bp[5], acl->data, acl->size); if (acl->size != size) memset((void *)&bp[5] + acl->size, 0, size - acl->size); - trace_afs_make_fs_call(call, &vnode->fid); - afs_make_call(&fc->ac, call, GFP_KERNEL); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_KERNEL); } diff --git a/fs/afs/inode.c b/fs/afs/inode.c index d2dbb3aef611..94675acb6a3a 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -67,16 +67,19 @@ static void afs_set_i_size(struct afs_vnode *vnode, u64 size) /* * Initialise an inode from the vnode status. */ -static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key, - struct afs_cb_interest *cbi, - struct afs_vnode *parent_vnode, - struct afs_status_cb *scb) +static int afs_inode_init_from_status(struct afs_operation *op, + struct afs_vnode_param *vp, + struct afs_vnode *vnode) { struct afs_cb_interest *old_cbi = NULL; - struct afs_file_status *status = &scb->status; + struct afs_file_status *status = &vp->scb.status; struct inode *inode = AFS_VNODE_TO_I(vnode); struct timespec64 t; + _enter("{%llx:%llu.%u} %s", + vp->fid.vid, vp->fid.vnode, vp->fid.unique, + op->type ? op->type->name : "???"); + _debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu", status->type, status->nlink, @@ -86,12 +89,15 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key, write_seqlock(&vnode->cb_lock); + vnode->cb_v_break = op->cb_v_break; + vnode->cb_s_break = op->cb_s_break; vnode->status = *status; t = status->mtime_client; inode->i_ctime = t; inode->i_mtime = t; inode->i_atime = t; + inode->i_flags |= S_NOATIME; inode->i_uid = make_kuid(&init_user_ns, status->owner); inode->i_gid = make_kgid(&init_user_ns, status->group); set_nlink(&vnode->vfs_inode, status->nlink); @@ -128,7 +134,7 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key, inode_nohighmem(inode); break; default: - dump_vnode(vnode, parent_vnode); + dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL); write_sequnlock(&vnode->cb_lock); return afs_protocol_error(NULL, afs_eproto_file_type); } @@ -138,16 +144,17 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key, vnode->invalid_before = status->data_version; inode_set_iversion_raw(&vnode->vfs_inode, status->data_version); - if (!scb->have_cb) { + if (!vp->scb.have_cb) { /* it's a symlink we just created (the fileserver * didn't give us a callback) */ vnode->cb_expires_at = ktime_get_real_seconds(); } else { - vnode->cb_expires_at = scb->callback.expires_at; + vnode->cb_expires_at = vp->scb.callback.expires_at; old_cbi = rcu_dereference_protected(vnode->cb_interest, lockdep_is_held(&vnode->cb_lock.lock)); - if (cbi != old_cbi) - rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(cbi)); + if (op->cbi != old_cbi) + rcu_assign_pointer(vnode->cb_interest, + afs_get_cb_interest(op->cbi)); else old_cbi = NULL; set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); @@ -161,16 +168,19 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key, /* * Update the core inode struct from a returned status record. */ -static void afs_apply_status(struct afs_operation *fc, - struct afs_vnode *vnode, - struct afs_status_cb *scb, - const afs_dataversion_t *expected_version) +static void afs_apply_status(struct afs_operation *op, + struct afs_vnode_param *vp) { - struct afs_file_status *status = &scb->status; + struct afs_file_status *status = &vp->scb.status; + struct afs_vnode *vnode = vp->vnode; struct timespec64 t; umode_t mode; bool data_changed = false; + _enter("{%llx:%llu.%u} %s", + vp->fid.vid, vp->fid.vnode, vp->fid.unique, + op->type ? op->type->name : "???"); + BUG_ON(test_bit(AFS_VNODE_UNSET, &vnode->flags)); if (status->type != vnode->status.type) { @@ -209,14 +219,13 @@ static void afs_apply_status(struct afs_operation *fc, vnode->status = *status; - if (expected_version && - *expected_version != status->data_version) { + if (vp->dv_before + vp->dv_delta != status->data_version) { if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) pr_warn("kAFS: vnode modified {%llx:%llu} %llx->%llx %s\n", vnode->fid.vid, vnode->fid.vnode, - (unsigned long long)*expected_version, + (unsigned long long)vp->dv_before + vp->dv_delta, (unsigned long long)status->data_version, - fc->type ? fc->type->name : "???"); + op->type ? op->type->name : "???"); vnode->invalid_before = status->data_version; if (vnode->status.type == AFS_FTYPE_DIR) { @@ -243,20 +252,19 @@ static void afs_apply_status(struct afs_operation *fc, /* * Apply a callback to a vnode. */ -static void afs_apply_callback(struct afs_operation *fc, - struct afs_vnode *vnode, - struct afs_status_cb *scb, - unsigned int cb_break) +static void afs_apply_callback(struct afs_operation *op, + struct afs_vnode_param *vp) { struct afs_cb_interest *old; - struct afs_callback *cb = &scb->callback; + struct afs_callback *cb = &vp->scb.callback; + struct afs_vnode *vnode = vp->vnode; - if (!afs_cb_is_broken(cb_break, vnode, fc->cbi)) { + if (!afs_cb_is_broken(vp->cb_break_before, vnode, op->cbi)) { vnode->cb_expires_at = cb->expires_at; old = rcu_dereference_protected(vnode->cb_interest, lockdep_is_held(&vnode->cb_lock.lock)); - if (old != fc->cbi) { - rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(fc->cbi)); + if (old != op->cbi) { + rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(op->cbi)); afs_put_cb_interest(afs_v2net(vnode), old); } set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); @@ -267,106 +275,108 @@ static void afs_apply_callback(struct afs_operation *fc, * Apply the received status and callback to an inode all in the same critical * section to avoid races with afs_validate(). */ -void afs_vnode_commit_status(struct afs_operation *fc, - struct afs_vnode *vnode, - unsigned int cb_break, - const afs_dataversion_t *expected_version, - struct afs_status_cb *scb) +void afs_vnode_commit_status(struct afs_operation *op, struct afs_vnode_param *vp) { - if (fc->ac.error != 0) - return; + struct afs_vnode *vnode = vp->vnode; + + _enter(""); + + ASSERTCMP(op->error, ==, 0); write_seqlock(&vnode->cb_lock); - if (scb->have_error) { - if (scb->status.abort_code == VNOVNODE) { + if (vp->scb.have_error) { + if (vp->scb.status.abort_code == VNOVNODE) { set_bit(AFS_VNODE_DELETED, &vnode->flags); clear_nlink(&vnode->vfs_inode); __afs_break_callback(vnode, afs_cb_break_for_deleted); } } else { - if (scb->have_status) - afs_apply_status(fc, vnode, scb, expected_version); - if (scb->have_cb) - afs_apply_callback(fc, vnode, scb, cb_break); + if (vp->scb.have_status) + afs_apply_status(op, vp); + if (vp->scb.have_cb) + afs_apply_callback(op, vp); } write_sequnlock(&vnode->cb_lock); - if (fc->ac.error == 0 && scb->have_status) - afs_cache_permit(vnode, fc->key, cb_break, scb); + if (op->error == 0 && vp->scb.have_status) + afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb); } +static void afs_fetch_status_success(struct afs_operation *op) +{ + struct afs_vnode_param *vp = &op->file[0]; + struct afs_vnode *vnode = vp->vnode; + int ret; + + if (vnode->vfs_inode.i_state & I_NEW) { + ret = afs_inode_init_from_status(op, vp, vnode); + op->error = ret; + if (ret == 0) + afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb); + } else { + afs_vnode_commit_status(op, vp); + } +} + +static const struct afs_operation_ops afs_fetch_status_operation = { + .issue_afs_rpc = afs_fs_fetch_status, + .issue_yfs_rpc = yfs_fs_fetch_status, + .success = afs_fetch_status_success, +}; + /* * Fetch file status from the volume. */ int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool is_new, afs_access_t *_caller_access) { - struct afs_status_cb *scb; - struct afs_operation fc; - int ret; + struct afs_operation *op; _enter("%s,{%llx:%llu.%u,S=%lx}", vnode->volume->name, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, vnode->flags); - scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - return -ENOMEM; + op = afs_alloc_operation(key, vnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, vnode, key, true)) { - afs_dataversion_t data_version = vnode->status.data_version; + afs_op_set_vnode(op, 0, vnode); - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(vnode); - afs_fs_fetch_file_status(&fc, scb, NULL); - } - - if (fc.error) { - /* Do nothing. */ - } else if (is_new) { - ret = afs_inode_init_from_status(vnode, key, fc.cbi, - NULL, scb); - fc.error = ret; - if (ret == 0) - afs_cache_permit(vnode, key, fc.cb_break, scb); - } else { - afs_vnode_commit_status(&fc, vnode, fc.cb_break, - &data_version, scb); - } - afs_check_for_remote_deletion(&fc, vnode); - ret = afs_end_vnode_operation(&fc); - } + op->nr_files = 1; + op->ops = &afs_fetch_status_operation; + afs_begin_vnode_operation(op); + afs_wait_for_operation(op); - if (ret == 0 && _caller_access) - *_caller_access = scb->status.caller_access; - kfree(scb); - _leave(" = %d", ret); - return ret; + if (_caller_access) + *_caller_access = op->file[0].scb.status.caller_access; + return afs_put_operation(op); } /* - * iget5() comparator + * ilookup() comparator */ -int afs_iget5_test(struct inode *inode, void *opaque) +int afs_ilookup5_test_by_fid(struct inode *inode, void *opaque) { - struct afs_iget_data *iget_data = opaque; struct afs_vnode *vnode = AFS_FS_I(inode); + struct afs_fid *fid = opaque; - return memcmp(&vnode->fid, &iget_data->fid, sizeof(iget_data->fid)) == 0; + return (fid->vnode == vnode->fid.vnode && + fid->vnode_hi == vnode->fid.vnode_hi && + fid->unique == vnode->fid.unique); } /* - * iget5() comparator for inode created by autocell operations - * - * These pseudo inodes don't match anything. + * iget5() comparator */ -static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque) +static int afs_iget5_test(struct inode *inode, void *opaque) { - return 0; + struct afs_vnode_param *vp = opaque; + //struct afs_vnode *vnode = AFS_FS_I(inode); + + return afs_ilookup5_test_by_fid(inode, &vp->fid); } /* @@ -374,98 +384,21 @@ static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque) */ static int afs_iget5_set(struct inode *inode, void *opaque) { - struct afs_iget_data *iget_data = opaque; + struct afs_vnode_param *vp = opaque; + struct afs_super_info *as = AFS_FS_S(inode->i_sb); struct afs_vnode *vnode = AFS_FS_I(inode); - vnode->fid = iget_data->fid; - vnode->volume = iget_data->volume; - vnode->cb_v_break = iget_data->cb_v_break; - vnode->cb_s_break = iget_data->cb_s_break; + vnode->volume = as->volume; + vnode->fid = vp->fid; /* YFS supports 96-bit vnode IDs, but Linux only supports * 64-bit inode numbers. */ - inode->i_ino = iget_data->fid.vnode; - inode->i_generation = iget_data->fid.unique; + inode->i_ino = vnode->fid.vnode; + inode->i_generation = vnode->fid.unique; return 0; } -/* - * Create an inode for a dynamic root directory or an autocell dynamic - * automount dir. - */ -struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root) -{ - struct afs_super_info *as; - struct afs_vnode *vnode; - struct inode *inode; - static atomic_t afs_autocell_ino; - - struct afs_iget_data iget_data = { - .cb_v_break = 0, - .cb_s_break = 0, - }; - - _enter(""); - - as = sb->s_fs_info; - if (as->volume) { - iget_data.volume = as->volume; - iget_data.fid.vid = as->volume->vid; - } - if (root) { - iget_data.fid.vnode = 1; - iget_data.fid.unique = 1; - } else { - iget_data.fid.vnode = atomic_inc_return(&afs_autocell_ino); - iget_data.fid.unique = 0; - } - - inode = iget5_locked(sb, iget_data.fid.vnode, - afs_iget5_pseudo_dir_test, afs_iget5_set, - &iget_data); - if (!inode) { - _leave(" = -ENOMEM"); - return ERR_PTR(-ENOMEM); - } - - _debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }", - inode, inode->i_ino, iget_data.fid.vid, iget_data.fid.vnode, - iget_data.fid.unique); - - vnode = AFS_FS_I(inode); - - /* there shouldn't be an existing inode */ - BUG_ON(!(inode->i_state & I_NEW)); - - inode->i_size = 0; - inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; - if (root) { - inode->i_op = &afs_dynroot_inode_operations; - inode->i_fop = &simple_dir_operations; - } else { - inode->i_op = &afs_autocell_inode_operations; - } - set_nlink(inode, 2); - inode->i_uid = GLOBAL_ROOT_UID; - inode->i_gid = GLOBAL_ROOT_GID; - inode->i_ctime = inode->i_atime = inode->i_mtime = current_time(inode); - inode->i_blocks = 0; - inode_set_iversion_raw(inode, 0); - inode->i_generation = 0; - - set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); - if (!root) { - set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); - inode->i_flags |= S_AUTOMOUNT; - } - - inode->i_flags |= S_NOATIME; - unlock_new_inode(inode); - _leave(" = %p", inode); - return inode; -} - /* * Get a cache cookie for an inode. */ @@ -501,58 +434,41 @@ static void afs_get_inode_cache(struct afs_vnode *vnode) /* * inode retrieval */ -struct inode *afs_iget(struct super_block *sb, struct key *key, - struct afs_iget_data *iget_data, - struct afs_status_cb *scb, - struct afs_cb_interest *cbi, - struct afs_vnode *parent_vnode) +struct inode *afs_iget(struct afs_operation *op, struct afs_vnode_param *vp) { - struct afs_super_info *as; + struct afs_vnode_param *dvp = &op->file[0]; + struct super_block *sb = dvp->vnode->vfs_inode.i_sb; struct afs_vnode *vnode; - struct afs_fid *fid = &iget_data->fid; struct inode *inode; int ret; - _enter(",{%llx:%llu.%u},,", fid->vid, fid->vnode, fid->unique); + _enter(",{%llx:%llu.%u},,", vp->fid.vid, vp->fid.vnode, vp->fid.unique); - as = sb->s_fs_info; - iget_data->volume = as->volume; - - inode = iget5_locked(sb, fid->vnode, afs_iget5_test, afs_iget5_set, - iget_data); + inode = iget5_locked(sb, vp->fid.vnode, afs_iget5_test, afs_iget5_set, vp); if (!inode) { _leave(" = -ENOMEM"); return ERR_PTR(-ENOMEM); } - _debug("GOT INODE %p { vl=%llx vn=%llx, u=%x }", - inode, fid->vid, fid->vnode, fid->unique); - vnode = AFS_FS_I(inode); + _debug("GOT INODE %p { vl=%llx vn=%llx, u=%x }", + inode, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); + /* deal with an existing inode */ if (!(inode->i_state & I_NEW)) { _leave(" = %p", inode); return inode; } - if (!scb) { - /* it's a remotely extant inode */ - ret = afs_fetch_status(vnode, key, true, NULL); - if (ret < 0) - goto bad_inode; - } else { - ret = afs_inode_init_from_status(vnode, key, cbi, parent_vnode, - scb); - if (ret < 0) - goto bad_inode; - } + ret = afs_inode_init_from_status(op, vp, vnode); + if (ret < 0) + goto bad_inode; afs_get_inode_cache(vnode); /* success */ clear_bit(AFS_VNODE_UNSET, &vnode->flags); - inode->i_flags |= S_NOATIME; unlock_new_inode(inode); _leave(" = %p", inode); return inode; @@ -564,6 +480,74 @@ bad_inode: return ERR_PTR(ret); } +static int afs_iget5_set_root(struct inode *inode, void *opaque) +{ + struct afs_super_info *as = AFS_FS_S(inode->i_sb); + struct afs_vnode *vnode = AFS_FS_I(inode); + + vnode->volume = as->volume; + vnode->fid.vid = as->volume->vid, + vnode->fid.vnode = 1; + vnode->fid.unique = 1; + inode->i_ino = 1; + inode->i_generation = 1; + return 0; +} + +/* + * Set up the root inode for a volume. This is always vnode 1, unique 1 within + * the volume. + */ +struct inode *afs_root_iget(struct super_block *sb, struct key *key) +{ + struct afs_super_info *as = AFS_FS_S(sb); + struct afs_operation *op; + struct afs_vnode *vnode; + struct inode *inode; + int ret; + + _enter(",{%llx},,", as->volume->vid); + + inode = iget5_locked(sb, 1, NULL, afs_iget5_set_root, NULL); + if (!inode) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + _debug("GOT ROOT INODE %p { vl=%llx }", inode, as->volume->vid); + + BUG_ON(!(inode->i_state & I_NEW)); + + vnode = AFS_FS_I(inode); + vnode->cb_v_break = as->volume->cb_v_break, + + op = afs_alloc_operation(key, as->volume); + if (IS_ERR(op)) { + ret = PTR_ERR(op); + goto error; + } + + afs_op_set_vnode(op, 0, vnode); + + op->nr_files = 1; + op->ops = &afs_fetch_status_operation; + ret = afs_do_sync_operation(op); + if (ret < 0) + goto error; + + afs_get_inode_cache(vnode); + + clear_bit(AFS_VNODE_UNSET, &vnode->flags); + unlock_new_inode(inode); + _leave(" = %p", inode); + return inode; + +error: + iget_failed(inode); + _leave(" = %d [bad]", ret); + return ERR_PTR(ret); +} + /* * mark the data attached to an inode as obsolete due to a write on the server * - might also want to ditch all the outstanding writes and dirty pages @@ -808,16 +792,24 @@ void afs_evict_inode(struct inode *inode) _leave(""); } +static void afs_setattr_success(struct afs_operation *op) +{ + afs_vnode_commit_status(op, &op->file[0]); +} + +static const struct afs_operation_ops afs_setattr_operation = { + .issue_afs_rpc = afs_fs_setattr, + .issue_yfs_rpc = yfs_fs_setattr, + .success = afs_setattr_success, +}; + /* * set the attributes of an inode */ int afs_setattr(struct dentry *dentry, struct iattr *attr) { - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); - struct key *key; - int ret = -ENOMEM; _enter("{%llx:%llu},{n=%pd},%x", vnode->fid.vid, vnode->fid.vnode, dentry, @@ -829,48 +821,22 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr) return 0; } - scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL); - if (!scb) - goto error; - /* flush any dirty data outstanding on a regular file */ if (S_ISREG(vnode->vfs_inode.i_mode)) filemap_write_and_wait(vnode->vfs_inode.i_mapping); - if (attr->ia_valid & ATTR_FILE) { - key = afs_file_key(attr->ia_file); - } else { - key = afs_request_key(vnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error_scb; - } - } - - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, vnode, key, false)) { - afs_dataversion_t data_version = vnode->status.data_version; - - if (attr->ia_valid & ATTR_SIZE) - data_version++; - - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(vnode); - afs_fs_setattr(&fc, attr, scb); - } + op = afs_alloc_operation(((attr->ia_valid & ATTR_FILE) ? + afs_file_key(attr->ia_file) : NULL), + vnode->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - 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); - } + afs_op_set_vnode(op, 0, vnode); + op->setattr.attr = attr; - if (!(attr->ia_valid & ATTR_FILE)) - key_put(key); + if (attr->ia_valid & ATTR_SIZE) + op->file[0].dv_delta = 1; -error_scb: - kfree(scb); -error: - _leave(" = %d", ret); - return ret; + op->ops = &afs_setattr_operation; + return afs_do_sync_operation(op); } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 0551dedb0371..4b8ac049fc17 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -59,13 +59,6 @@ struct afs_fs_context { struct key *key; /* key to use for secure mounting */ }; -struct afs_iget_data { - struct afs_fid fid; - struct afs_volume *volume; /* volume on which resides */ - unsigned int cb_v_break; /* Pre-fetch volume break count */ - unsigned int cb_s_break; /* Pre-fetch server break count */ -}; - enum afs_call_state { AFS_CALL_CL_REQUESTING, /* Client: Request is being sent */ AFS_CALL_CL_AWAIT_REPLY, /* Client: Awaiting reply */ @@ -111,9 +104,7 @@ struct afs_call { struct afs_server *server; /* The fileserver record if fs op (pins ref) */ struct afs_vlserver *vlserver; /* The vlserver record if vl op */ struct afs_cb_interest *cbi; /* Callback interest for server used */ - struct afs_vnode *lvnode; /* vnode being locked */ void *request; /* request data (first part) */ - struct address_space *mapping; /* Pages being written from */ struct iov_iter def_iter; /* Default buffer/data iterator */ struct iov_iter *iter; /* Iterator currently in use */ union { /* Convenience for ->def_iter */ @@ -125,18 +116,9 @@ struct afs_call { long ret0; /* Value to reply with instead of 0 */ struct afs_addr_list *ret_alist; struct afs_vldb_entry *ret_vldb; - struct afs_acl *ret_acl; }; - struct afs_fid *out_fid; - struct afs_status_cb *out_dir_scb; - struct afs_status_cb *out_scb; - struct yfs_acl *out_yacl; - struct afs_volsync *out_volsync; - struct afs_volume_status *out_volstatus; - struct afs_read *read_request; + struct afs_operation *op; unsigned int server_index; - pgoff_t first; /* first page in mapping to deal with */ - pgoff_t last; /* last page in mapping to deal with */ atomic_t usage; enum afs_call_state state; spinlock_t state_lock; @@ -146,11 +128,7 @@ struct afs_call { unsigned int max_lifespan; /* Maximum lifespan to set if not 0 */ unsigned request_size; /* size of request data */ unsigned reply_max; /* maximum size of reply */ - unsigned first_offset; /* offset into mapping[first] */ - union { - unsigned last_to; /* amount of mapping[last] */ - unsigned count2; /* count used in unmarshalling */ - }; + unsigned count2; /* count used in unmarshalling */ unsigned char unmarshall; /* unmarshalling phase */ unsigned char addr_ix; /* Address in ->alist */ bool drop_ref; /* T if need to drop ref for incoming call */ @@ -570,10 +548,7 @@ struct afs_cb_interest { struct afs_vol_interest *vol_interest; struct afs_server *server; /* Server on which this interest resides */ struct super_block *sb; /* Superblock on which inodes reside */ - union { - struct rcu_head rcu; - afs_volid_t vid; /* Volume ID to match */ - }; + struct rcu_head rcu; refcount_t usage; }; @@ -764,29 +739,116 @@ struct afs_vl_cursor { }; /* - * Cursor for iterating over a set of fileservers. + * Fileserver operation methods. + */ +struct afs_operation_ops { + void (*issue_afs_rpc)(struct afs_operation *op); + void (*issue_yfs_rpc)(struct afs_operation *op); + void (*success)(struct afs_operation *op); + void (*aborted)(struct afs_operation *op); + void (*edit_dir)(struct afs_operation *op); + void (*put)(struct afs_operation *op); +}; + +struct afs_vnode_param { + struct afs_vnode *vnode; + struct afs_fid fid; /* Fid to access */ + struct afs_status_cb scb; /* Returned status and callback promise */ + afs_dataversion_t dv_before; /* Data version before the call */ + unsigned int cb_break_before; /* cb_break + cb_s_break before the call */ + u8 dv_delta; /* Expected change in data version */ + bool put_vnode; /* T if we have a ref on the vnode */ + bool need_io_lock; /* T if we need the I/O lock on this */ +}; + +/* + * Fileserver operation wrapper, handling server and address rotation + * asynchronously. May make simultaneous calls to multiple servers. */ struct afs_operation { + struct afs_net *net; /* Network namespace */ + struct key *key; /* Key for the cell */ const struct afs_call_type *type; /* Type of call done */ + const struct afs_operation_ops *ops; + + /* Parameters/results for the operation */ + struct afs_volume *volume; /* Volume being accessed */ + struct afs_vnode_param file[2]; + struct afs_vnode_param *more_files; + struct afs_volsync volsync; + struct dentry *dentry; /* Dentry to be altered */ + struct dentry *dentry_2; /* Second dentry to be altered */ + struct timespec64 mtime; /* Modification time to record */ + short nr_files; /* Number of entries in file[], more_files */ + short error; + unsigned int abort_code; + unsigned int debug_id; + + unsigned int cb_v_break; /* Volume break counter before op */ + unsigned int cb_s_break; /* Server break counter before op */ + + union { + struct { + int which; /* Which ->file[] to fetch for */ + } fetch_status; + struct { + int reason; /* enum afs_edit_dir_reason */ + mode_t mode; + const char *symlink; + } create; + struct { + bool need_rehash; + } unlink; + struct { + struct dentry *rehash; + struct dentry *tmp; + bool new_negative; + } rename; + struct { + struct afs_read *req; + } fetch; + struct { + struct afs_vnode *lvnode; /* vnode being locked */ + afs_lock_type_t type; + } lock; + struct { + struct address_space *mapping; /* Pages being written from */ + pgoff_t first; /* first page in mapping to deal with */ + pgoff_t last; /* last page in mapping to deal with */ + unsigned first_offset; /* offset into mapping[first] */ + unsigned last_to; /* amount of mapping[last] */ + } store; + struct { + struct iattr *attr; + } setattr; + struct afs_acl *acl; + struct yfs_acl *yacl; + struct { + struct afs_volume_status vs; + struct kstatfs *buf; + } volstatus; + }; + + /* Fileserver iteration state */ struct afs_addr_cursor ac; - struct afs_vnode *vnode; struct afs_server_list *server_list; /* Current server list (pins ref) */ struct afs_cb_interest *cbi; /* Server on which this resides (pins ref) */ - struct key *key; /* Key for the server */ + struct afs_call *call; unsigned long untried; /* Bitmask of untried servers */ - unsigned int cb_break; /* cb_break + cb_s_break before the call */ - unsigned int cb_break_2; /* cb_break + cb_s_break (2nd vnode) */ short index; /* Current server */ - short error; - unsigned short flags; + unsigned short nr_iterations; /* Number of server iterations */ + + unsigned int flags; #define AFS_OPERATION_STOP 0x0001 /* Set to cease iteration */ #define AFS_OPERATION_VBUSY 0x0002 /* Set if seen VBUSY */ #define AFS_OPERATION_VMOVED 0x0004 /* Set if seen VMOVED */ #define AFS_OPERATION_VNOVOL 0x0008 /* Set if seen VNOVOL */ #define AFS_OPERATION_CUR_ONLY 0x0010 /* Set if current server only (file lock held) */ #define AFS_OPERATION_NO_VSLEEP 0x0020 /* Set to prevent sleep on VBUSY, VOFFLINE, ... */ -#define AFS_OPERATION_INTR 0x0040 /* Set if op is interruptible */ - unsigned short nr_iterations; /* Number of server iterations */ +#define AFS_OPERATION_UNINTR 0x0040 /* Set if op is uninterruptible */ +#define AFS_OPERATION_DOWNGRADE 0x0080 /* Set to retry with downgraded opcode */ +#define AFS_OPERATION_LOCK_0 0x0100 /* Set if have io_lock on file[0] */ +#define AFS_OPERATION_LOCK_1 0x0200 /* Set if have io_lock on file[1] */ }; /* @@ -958,46 +1020,61 @@ extern int afs_flock(struct file *, int, struct file_lock *); /* * fsclient.c */ -extern int afs_fs_fetch_file_status(struct afs_operation *, struct afs_status_cb *, - struct afs_volsync *); -extern int afs_fs_fetch_data(struct afs_operation *, struct afs_status_cb *, struct afs_read *); -extern int afs_fs_create(struct afs_operation *, const char *, umode_t, - struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); -extern int afs_fs_remove(struct afs_operation *, struct afs_vnode *, const char *, bool, - struct afs_status_cb *); -extern int afs_fs_link(struct afs_operation *, struct afs_vnode *, const char *, - struct afs_status_cb *, struct afs_status_cb *); -extern int afs_fs_symlink(struct afs_operation *, const char *, const char *, - struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); -extern int afs_fs_rename(struct afs_operation *, const char *, - struct afs_vnode *, const char *, - struct afs_status_cb *, struct afs_status_cb *); -extern int afs_fs_store_data(struct afs_operation *, struct address_space *, - pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *); -extern int afs_fs_setattr(struct afs_operation *, struct iattr *, struct afs_status_cb *); -extern int afs_fs_get_volume_status(struct afs_operation *, struct afs_volume_status *); -extern int afs_fs_set_lock(struct afs_operation *, afs_lock_type_t, struct afs_status_cb *); -extern int afs_fs_extend_lock(struct afs_operation *, struct afs_status_cb *); -extern int afs_fs_release_lock(struct afs_operation *, struct afs_status_cb *); +extern void afs_fs_fetch_status(struct afs_operation *); +extern void afs_fs_fetch_data(struct afs_operation *); +extern void afs_fs_create_file(struct afs_operation *); +extern void afs_fs_make_dir(struct afs_operation *); +extern void afs_fs_remove_file(struct afs_operation *); +extern void afs_fs_remove_dir(struct afs_operation *); +extern void afs_fs_link(struct afs_operation *); +extern void afs_fs_symlink(struct afs_operation *); +extern void afs_fs_rename(struct afs_operation *); +extern void afs_fs_store_data(struct afs_operation *); +extern void afs_fs_setattr(struct afs_operation *); +extern void afs_fs_get_volume_status(struct afs_operation *); +extern void afs_fs_set_lock(struct afs_operation *); +extern void afs_fs_extend_lock(struct afs_operation *); +extern void afs_fs_release_lock(struct afs_operation *); extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *, struct afs_addr_cursor *, struct key *); extern bool afs_fs_get_capabilities(struct afs_net *, struct afs_server *, struct afs_addr_cursor *, struct key *); -extern int afs_fs_inline_bulk_status(struct afs_operation *, struct afs_net *, - struct afs_fid *, struct afs_status_cb *, - unsigned int, struct afs_volsync *); -extern int afs_fs_fetch_status(struct afs_operation *, struct afs_net *, - struct afs_fid *, struct afs_status_cb *, - struct afs_volsync *); +extern void afs_fs_inline_bulk_status(struct afs_operation *); struct afs_acl { u32 size; u8 data[]; }; -extern struct afs_acl *afs_fs_fetch_acl(struct afs_operation *, struct afs_status_cb *); -extern int afs_fs_store_acl(struct afs_operation *, const struct afs_acl *, - struct afs_status_cb *); +extern void afs_fs_fetch_acl(struct afs_operation *); +extern void afs_fs_store_acl(struct afs_operation *); + +/* + * fs_operation.c + */ +extern struct afs_operation *afs_alloc_operation(struct key *, struct afs_volume *); +extern int afs_put_operation(struct afs_operation *); +extern bool afs_begin_vnode_operation(struct afs_operation *); +extern void afs_wait_for_operation(struct afs_operation *); +extern int afs_do_sync_operation(struct afs_operation *); + +static inline void afs_op_nomem(struct afs_operation *op) +{ + op->error = -ENOMEM; +} + +static inline void afs_op_set_vnode(struct afs_operation *op, unsigned int n, + struct afs_vnode *vnode) +{ + op->file[n].vnode = vnode; + op->file[n].need_io_lock = true; +} + +static inline void afs_op_set_fid(struct afs_operation *op, unsigned int n, + const struct afs_fid *fid) +{ + op->file[n].fid = *fid; +} /* * fs_probe.c @@ -1010,18 +1087,12 @@ extern void afs_fs_probe_dispatcher(struct work_struct *); /* * inode.c */ -extern void afs_vnode_commit_status(struct afs_operation *, - struct afs_vnode *, - unsigned int, - const afs_dataversion_t *, - struct afs_status_cb *); +extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode_param *); extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *); -extern int afs_iget5_test(struct inode *, void *); +extern int afs_ilookup5_test_by_fid(struct inode *, void *); extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool); -extern struct inode *afs_iget(struct super_block *, struct key *, - struct afs_iget_data *, struct afs_status_cb *, - struct afs_cb_interest *, - struct afs_vnode *); +extern struct inode *afs_iget(struct afs_operation *, struct afs_vnode_param *); +extern struct inode *afs_root_iget(struct super_block *, struct key *); extern void afs_zap_data(struct afs_vnode *); extern bool afs_check_validity(struct afs_vnode *); extern int afs_validate(struct afs_vnode *, struct key *); @@ -1109,11 +1180,9 @@ static inline void afs_put_sysnames(struct afs_sysnames *sysnames) {} /* * rotate.c */ -extern bool afs_begin_vnode_operation(struct afs_operation *, struct afs_vnode *, - struct key *, bool); extern bool afs_select_fileserver(struct afs_operation *); extern bool afs_select_current_fileserver(struct afs_operation *); -extern int afs_end_vnode_operation(struct afs_operation *); +extern void afs_dump_edestaddrreq(const struct afs_operation *); /* * rxrpc.c @@ -1135,10 +1204,16 @@ extern void afs_send_simple_reply(struct afs_call *, const void *, size_t); extern int afs_extract_data(struct afs_call *, bool); extern int afs_protocol_error(struct afs_call *, enum afs_eproto_cause); -static inline void afs_set_fc_call(struct afs_call *call, struct afs_operation *op) +static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *call, + gfp_t gfp) { - call->intr = op->flags & AFS_OPERATION_INTR; + op->call = call; op->type = call->type; + call->op = op; + call->key = op->key; + call->cbi = afs_get_cb_interest(op->cbi); + call->intr = !(op->flags & AFS_OPERATION_UNINTR); + afs_make_call(&op->ac, call, gfp); } static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size) @@ -1347,7 +1422,7 @@ extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *, /* * volume.c */ -static inline struct afs_volume *__afs_get_volume(struct afs_volume *volume) +static inline struct afs_volume *afs_get_volume(struct afs_volume *volume) { if (volume) atomic_inc(&volume->usage); @@ -1357,7 +1432,7 @@ static inline struct afs_volume *__afs_get_volume(struct afs_volume *volume) extern struct afs_volume *afs_create_volume(struct afs_fs_context *); extern void afs_activate_volume(struct afs_volume *); extern void afs_deactivate_volume(struct afs_volume *); -extern void afs_put_volume(struct afs_cell *, struct afs_volume *); +extern void afs_put_volume(struct afs_net *, struct afs_volume *); extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *); /* @@ -1387,36 +1462,24 @@ extern ssize_t afs_listxattr(struct dentry *, char *, size_t); /* * yfsclient.c */ -extern int yfs_fs_fetch_file_status(struct afs_operation *, struct afs_status_cb *, - struct afs_volsync *); -extern int yfs_fs_fetch_data(struct afs_operation *, struct afs_status_cb *, struct afs_read *); -extern int yfs_fs_create_file(struct afs_operation *, const char *, umode_t, struct afs_status_cb *, - struct afs_fid *, struct afs_status_cb *); -extern int yfs_fs_make_dir(struct afs_operation *, const char *, umode_t, struct afs_status_cb *, - struct afs_fid *, struct afs_status_cb *); -extern int yfs_fs_remove_file2(struct afs_operation *, struct afs_vnode *, const char *, - struct afs_status_cb *, struct afs_status_cb *); -extern int yfs_fs_remove(struct afs_operation *, struct afs_vnode *, const char *, bool, - struct afs_status_cb *); -extern int yfs_fs_link(struct afs_operation *, struct afs_vnode *, const char *, - struct afs_status_cb *, struct afs_status_cb *); -extern int yfs_fs_symlink(struct afs_operation *, const char *, const char *, - struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *); -extern int yfs_fs_rename(struct afs_operation *, const char *, struct afs_vnode *, const char *, - struct afs_status_cb *, struct afs_status_cb *); -extern int yfs_fs_store_data(struct afs_operation *, struct address_space *, - pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *); -extern int yfs_fs_setattr(struct afs_operation *, struct iattr *, struct afs_status_cb *); -extern int yfs_fs_get_volume_status(struct afs_operation *, struct afs_volume_status *); -extern int yfs_fs_set_lock(struct afs_operation *, afs_lock_type_t, struct afs_status_cb *); -extern int yfs_fs_extend_lock(struct afs_operation *, struct afs_status_cb *); -extern int yfs_fs_release_lock(struct afs_operation *, struct afs_status_cb *); -extern int yfs_fs_fetch_status(struct afs_operation *, struct afs_net *, - struct afs_fid *, struct afs_status_cb *, - struct afs_volsync *); -extern int yfs_fs_inline_bulk_status(struct afs_operation *, struct afs_net *, - struct afs_fid *, struct afs_status_cb *, - unsigned int, struct afs_volsync *); +extern void yfs_fs_fetch_file_status(struct afs_operation *); +extern void yfs_fs_fetch_data(struct afs_operation *); +extern void yfs_fs_create_file(struct afs_operation *); +extern void yfs_fs_make_dir(struct afs_operation *); +extern void yfs_fs_remove_file2(struct afs_operation *); +extern void yfs_fs_remove_file(struct afs_operation *); +extern void yfs_fs_remove_dir(struct afs_operation *); +extern void yfs_fs_link(struct afs_operation *); +extern void yfs_fs_symlink(struct afs_operation *); +extern void yfs_fs_rename(struct afs_operation *); +extern void yfs_fs_store_data(struct afs_operation *); +extern void yfs_fs_setattr(struct afs_operation *); +extern void yfs_fs_get_volume_status(struct afs_operation *); +extern void yfs_fs_set_lock(struct afs_operation *); +extern void yfs_fs_extend_lock(struct afs_operation *); +extern void yfs_fs_release_lock(struct afs_operation *); +extern void yfs_fs_fetch_status(struct afs_operation *); +extern void yfs_fs_inline_bulk_status(struct afs_operation *); struct yfs_acl { struct afs_acl *acl; /* Dir/file/symlink ACL */ @@ -1429,10 +1492,8 @@ struct yfs_acl { }; extern void yfs_free_opaque_acl(struct yfs_acl *); -extern struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_operation *, struct yfs_acl *, - struct afs_status_cb *); -extern int yfs_fs_store_opaque_acl2(struct afs_operation *, const struct afs_acl *, - struct afs_status_cb *); +extern void yfs_fs_fetch_opaque_acl(struct afs_operation *); +extern void yfs_fs_store_opaque_acl2(struct afs_operation *); /* * Miscellaneous inline functions. @@ -1450,12 +1511,26 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode) static inline void afs_check_for_remote_deletion(struct afs_operation *op, struct afs_vnode *vnode) { - if (op->ac.error == -ENOENT) { + if (op->error == -ENOENT) { set_bit(AFS_VNODE_DELETED, &vnode->flags); afs_break_callback(vnode, afs_cb_break_for_deleted); } } +/* + * Note that a dentry got changed. We need to set d_fsdata to the data version + * number derived from the result of the operation. It doesn't matter if + * d_fsdata goes backwards as we'll just revalidate. + */ +static inline void afs_update_dentry_version(struct afs_operation *op, + struct afs_vnode_param *dir_vp, + struct dentry *dentry) +{ + if (!op->error) + dentry->d_fsdata = + (void *)(unsigned long)dir_vp->scb.status.data_version; +} + static inline int afs_io_error(struct afs_call *call, enum afs_io_error where) { trace_afs_io_error(call->debug_id, -EIO, where); diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index c930033473f6..8c8dc2397c5d 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -14,37 +14,6 @@ #include "internal.h" #include "afs_fs.h" -/* - * Begin an operation on the fileserver. - * - * Fileserver operations are serialised on the server by vnode, so we serialise - * them here also using the io_lock. - */ -bool afs_begin_vnode_operation(struct afs_operation *op, struct afs_vnode *vnode, - struct key *key, bool intr) -{ - memset(op, 0, sizeof(*op)); - op->vnode = vnode; - op->key = key; - op->ac.error = SHRT_MAX; - op->error = -EDESTADDRREQ; - - if (intr) { - op->flags |= AFS_OPERATION_INTR; - if (mutex_lock_interruptible(&vnode->io_lock) < 0) { - op->error = -EINTR; - op->flags |= AFS_OPERATION_STOP; - return false; - } - } else { - mutex_lock(&vnode->io_lock); - } - - if (vnode->lock_state != AFS_VNODE_LOCK_NONE) - op->flags |= AFS_OPERATION_CUR_ONLY; - return true; -} - /* * Begin iteration through a server list, starting with the vnode's last used * server if possible, or the last recorded good server if not. @@ -55,9 +24,9 @@ static bool afs_start_fs_iteration(struct afs_operation *op, struct afs_cb_interest *cbi; int i; - read_lock(&vnode->volume->servers_lock); - op->server_list = afs_get_serverlist(vnode->volume->servers); - read_unlock(&vnode->volume->servers_lock); + read_lock(&op->volume->servers_lock); + op->server_list = afs_get_serverlist(op->volume->servers); + read_unlock(&op->volume->servers_lock); op->untried = (1UL << op->server_list->nr_servers) - 1; op->index = READ_ONCE(op->server_list->preferred); @@ -90,7 +59,7 @@ static bool afs_start_fs_iteration(struct afs_operation *op, vnode->cb_break++; write_sequnlock(&vnode->cb_lock); - afs_put_cb_interest(afs_v2net(vnode), cbi); + afs_put_cb_interest(op->net, cbi); cbi = NULL; } @@ -120,7 +89,7 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code) */ static bool afs_sleep_and_retry(struct afs_operation *op) { - if (op->flags & AFS_OPERATION_INTR) { + if (!(op->flags & AFS_OPERATION_UNINTR)) { msleep_interruptible(1000); if (signal_pending(current)) { op->error = -ERESTARTSYS; @@ -141,7 +110,7 @@ bool afs_select_fileserver(struct afs_operation *op) { struct afs_addr_list *alist; struct afs_server *server; - struct afs_vnode *vnode = op->vnode; + struct afs_vnode *vnode = op->file[0].vnode; struct afs_error e; u32 rtt; int error = op->ac.error, i; @@ -187,16 +156,16 @@ bool afs_select_fileserver(struct afs_operation *op) goto next_server; } - write_lock(&vnode->volume->servers_lock); + write_lock(&op->volume->servers_lock); op->server_list->vnovol_mask |= 1 << op->index; - write_unlock(&vnode->volume->servers_lock); + write_unlock(&op->volume->servers_lock); - set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags); - error = afs_check_volume_status(vnode->volume, op); + set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags); + error = afs_check_volume_status(op->volume, op); if (error < 0) goto failed_set_error; - if (test_bit(AFS_VOLUME_DELETED, &vnode->volume->flags)) { + if (test_bit(AFS_VOLUME_DELETED, &op->volume->flags)) { op->error = -ENOMEDIUM; goto failed; } @@ -204,7 +173,7 @@ bool afs_select_fileserver(struct afs_operation *op) /* If the server list didn't change, then assume that * it's the fileserver having trouble. */ - if (vnode->volume->servers == op->server_list) { + if (op->volume->servers == op->server_list) { op->error = -EREMOTEIO; goto next_server; } @@ -224,9 +193,9 @@ bool afs_select_fileserver(struct afs_operation *op) goto next_server; case VOFFLINE: - if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags)) { - afs_busy(vnode->volume, op->ac.abort_code); - clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags); + if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &op->volume->flags)) { + afs_busy(op->volume, op->ac.abort_code); + clear_bit(AFS_VOLUME_BUSY, &op->volume->flags); } if (op->flags & AFS_OPERATION_NO_VSLEEP) { op->error = -EADV; @@ -248,9 +217,9 @@ bool afs_select_fileserver(struct afs_operation *op) op->error = -EBUSY; goto failed; } - if (!test_and_set_bit(AFS_VOLUME_BUSY, &vnode->volume->flags)) { - afs_busy(vnode->volume, op->ac.abort_code); - clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags); + if (!test_and_set_bit(AFS_VOLUME_BUSY, &op->volume->flags)) { + afs_busy(op->volume, op->ac.abort_code); + clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags); } busy: if (op->flags & AFS_OPERATION_CUR_ONLY) { @@ -279,9 +248,9 @@ bool afs_select_fileserver(struct afs_operation *op) } op->flags |= AFS_OPERATION_VMOVED; - set_bit(AFS_VOLUME_WAIT, &vnode->volume->flags); - set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags); - error = afs_check_volume_status(vnode->volume, op); + set_bit(AFS_VOLUME_WAIT, &op->volume->flags); + set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags); + error = afs_check_volume_status(op->volume, op); if (error < 0) goto failed_set_error; @@ -294,7 +263,7 @@ bool afs_select_fileserver(struct afs_operation *op) * * TODO: Retry a few times with sleeps. */ - if (vnode->volume->servers == op->server_list) { + if (op->volume->servers == op->server_list) { op->error = -ENOMEDIUM; goto failed; } @@ -302,8 +271,8 @@ bool afs_select_fileserver(struct afs_operation *op) goto restart_from_beginning; default: - clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags); - clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags); + clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags); + clear_bit(AFS_VOLUME_BUSY, &op->volume->flags); op->error = afs_abort_to_error(op->ac.abort_code); goto failed; } @@ -332,23 +301,23 @@ bool afs_select_fileserver(struct afs_operation *op) restart_from_beginning: _debug("restart"); afs_end_cursor(&op->ac); - afs_put_cb_interest(afs_v2net(vnode), op->cbi); + afs_put_cb_interest(op->net, op->cbi); op->cbi = NULL; - afs_put_serverlist(afs_v2net(vnode), op->server_list); + afs_put_serverlist(op->net, op->server_list); op->server_list = NULL; start: _debug("start"); /* See if we need to do an update of the volume record. Note that the * volume may have moved or even have been deleted. */ - error = afs_check_volume_status(vnode->volume, op); + error = afs_check_volume_status(op->volume, op); if (error < 0) goto failed_set_error; if (!afs_start_fs_iteration(op, vnode)) goto failed; - _debug("__ VOL %llx __", vnode->volume->vid); + _debug("__ VOL %llx __", op->volume->vid); pick_server: _debug("pick [%lx]", op->untried); @@ -364,7 +333,7 @@ pick_server: _debug("cbi %u", op->index); if (test_bit(op->index, &op->untried)) goto selected_server; - afs_put_cb_interest(afs_v2net(vnode), op->cbi); + afs_put_cb_interest(op->net, op->cbi); op->cbi = NULL; _debug("nocbi"); } @@ -482,26 +451,21 @@ failed: */ bool afs_select_current_fileserver(struct afs_operation *op) { - struct afs_vnode *vnode = op->vnode; struct afs_cb_interest *cbi; struct afs_addr_list *alist; int error = op->ac.error; _enter(""); - cbi = rcu_dereference_protected(vnode->cb_interest, - lockdep_is_held(&vnode->io_lock)); - switch (error) { case SHRT_MAX: + cbi = op->cbi; if (!cbi) { op->error = -ESTALE; op->flags |= AFS_OPERATION_STOP; return false; } - op->cbi = afs_get_cb_interest(cbi); - read_lock(&cbi->server->fs_lock); alist = rcu_dereference_protected(cbi->server->addresses, lockdep_is_held(&cbi->server->fs_lock)); @@ -561,7 +525,7 @@ iterate_address: /* * Dump cursor state in the case of the error being EDESTADDRREQ. */ -static void afs_dump_edestaddrreq(const struct afs_operation *op) +void afs_dump_edestaddrreq(const struct afs_operation *op) { static int count; int i; @@ -573,8 +537,9 @@ static void afs_dump_edestaddrreq(const struct afs_operation *op) rcu_read_lock(); pr_notice("EDESTADDR occurred\n"); - pr_notice("FC: cbb=%x cbb2=%x fl=%hx err=%hd\n", - op->cb_break, op->cb_break_2, op->flags, op->error); + pr_notice("FC: cbb=%x cbb2=%x fl=%x err=%hd\n", + op->file[0].cb_break_before, + op->file[1].cb_break_before, op->flags, op->error); pr_notice("FC: ut=%lx ix=%d ni=%u\n", op->untried, op->index, op->nr_iterations); @@ -606,28 +571,3 @@ static void afs_dump_edestaddrreq(const struct afs_operation *op) op->ac.responded, op->ac.nr_iterations); rcu_read_unlock(); } - -/* - * Tidy up a filesystem cursor and unlock the vnode. - */ -int afs_end_vnode_operation(struct afs_operation *op) -{ - struct afs_net *net = afs_v2net(op->vnode); - - if (op->error == -EDESTADDRREQ || - op->error == -EADDRNOTAVAIL || - op->error == -ENETUNREACH || - op->error == -EHOSTUNREACH) - afs_dump_edestaddrreq(op); - - mutex_unlock(&op->vnode->io_lock); - - afs_end_cursor(&op->ac); - afs_put_cb_interest(net, op->cbi); - afs_put_serverlist(net, op->server_list); - - if (op->error == -ECONNABORTED) - op->error = afs_abort_to_error(op->ac.abort_code); - - return op->error; -} diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 00b87bac4fec..bd4d8e5efe59 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -283,18 +283,19 @@ static void afs_load_bvec(struct afs_call *call, struct msghdr *msg, struct bio_vec *bv, pgoff_t first, pgoff_t last, unsigned offset) { + struct afs_operation *op = call->op; struct page *pages[AFS_BVEC_MAX]; unsigned int nr, n, i, to, bytes = 0; nr = min_t(pgoff_t, last - first + 1, AFS_BVEC_MAX); - n = find_get_pages_contig(call->mapping, first, nr, pages); + n = find_get_pages_contig(op->store.mapping, first, nr, pages); ASSERTCMP(n, ==, nr); msg->msg_flags |= MSG_MORE; for (i = 0; i < nr; i++) { to = PAGE_SIZE; if (first + i >= last) { - to = call->last_to; + to = op->store.last_to; msg->msg_flags &= ~MSG_MORE; } bv[i].bv_page = pages[i]; @@ -324,13 +325,14 @@ static void afs_notify_end_request_tx(struct sock *sock, */ static int afs_send_pages(struct afs_call *call, struct msghdr *msg) { + struct afs_operation *op = call->op; struct bio_vec bv[AFS_BVEC_MAX]; unsigned int bytes, nr, loop, offset; - pgoff_t first = call->first, last = call->last; + pgoff_t first = op->store.first, last = op->store.last; int ret; - offset = call->first_offset; - call->first_offset = 0; + offset = op->store.first_offset; + op->store.first_offset = 0; do { afs_load_bvec(call, msg, bv, first, last, offset); @@ -340,7 +342,7 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg) bytes = msg->msg_iter.count; nr = msg->msg_iter.nr_segs; - ret = rxrpc_kernel_send_data(call->net->socket, call->rxcall, msg, + ret = rxrpc_kernel_send_data(op->net->socket, call->rxcall, msg, bytes, afs_notify_end_request_tx); for (loop = 0; loop < nr; loop++) put_page(bv[loop].bv_page); @@ -350,7 +352,7 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg) first += nr; } while (first <= last); - trace_afs_sent_pages(call, call->first, last, first, ret); + trace_afs_sent_pages(call, op->store.first, last, first, ret); return ret; } @@ -385,16 +387,18 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp) */ tx_total_len = call->request_size; if (call->send_pages) { - if (call->last == call->first) { - tx_total_len += call->last_to - call->first_offset; + struct afs_operation *op = call->op; + + if (op->store.last == op->store.first) { + tx_total_len += op->store.last_to - op->store.first_offset; } else { /* It looks mathematically like you should be able to * combine the following lines with the ones above, but * unsigned arithmetic is fun when it wraps... */ - tx_total_len += PAGE_SIZE - call->first_offset; - tx_total_len += call->last_to; - tx_total_len += (call->last - call->first - 1) * PAGE_SIZE; + tx_total_len += PAGE_SIZE - op->store.first_offset; + tx_total_len += op->store.last_to; + tx_total_len += (op->store.last - op->store.first - 1) * PAGE_SIZE; } } diff --git a/fs/afs/server.c b/fs/afs/server.c index 3008f2ecfeee..1c1e315094ae 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -424,10 +424,7 @@ static void __afs_put_server(struct afs_net *net, struct afs_server *server) afs_dec_servers_outstanding(net); } -/* - * destroy a dead server - */ -static void afs_destroy_server(struct afs_net *net, struct afs_server *server) +static void afs_give_up_callbacks(struct afs_net *net, struct afs_server *server) { struct afs_addr_list *alist = rcu_access_pointer(server->addresses); struct afs_addr_cursor ac = { @@ -436,8 +433,16 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server) .error = 0, }; + afs_fs_give_up_all_callbacks(net, server, &ac, NULL); +} + +/* + * destroy a dead server + */ +static void afs_destroy_server(struct afs_net *net, struct afs_server *server) +{ if (test_bit(AFS_SERVER_FL_MAY_HAVE_CB, &server->flags)) - afs_fs_give_up_all_callbacks(net, server, &ac, NULL); + afs_give_up_callbacks(net, server); afs_put_server(net, server, afs_server_trace_destroy); } @@ -571,7 +576,8 @@ void afs_purge_servers(struct afs_net *net) /* * Get an update for a server's address list. */ -static noinline bool afs_update_server_record(struct afs_operation *fc, struct afs_server *server) +static noinline bool afs_update_server_record(struct afs_operation *op, + struct afs_server *server) { struct afs_addr_list *alist, *discard; @@ -580,18 +586,17 @@ static noinline bool afs_update_server_record(struct afs_operation *fc, struct a trace_afs_server(server, atomic_read(&server->ref), atomic_read(&server->active), afs_server_trace_update); - alist = afs_vl_lookup_addrs(fc->vnode->volume->cell, fc->key, - &server->uuid); + alist = afs_vl_lookup_addrs(op->volume->cell, op->key, &server->uuid); if (IS_ERR(alist)) { if ((PTR_ERR(alist) == -ERESTARTSYS || PTR_ERR(alist) == -EINTR) && - !(fc->flags & AFS_OPERATION_INTR) && + (op->flags & AFS_OPERATION_UNINTR) && server->addresses) { _leave(" = t [intr]"); return true; } - fc->error = PTR_ERR(alist); - _leave(" = f [%d]", fc->error); + op->error = PTR_ERR(alist); + _leave(" = f [%d]", op->error); return false; } @@ -613,7 +618,7 @@ static noinline bool afs_update_server_record(struct afs_operation *fc, struct a /* * See if a server's address list needs updating. */ -bool afs_check_server_record(struct afs_operation *fc, struct afs_server *server) +bool afs_check_server_record(struct afs_operation *op, struct afs_server *server) { bool success; int ret, retries = 0; @@ -633,7 +638,7 @@ retry: update: if (!test_and_set_bit_lock(AFS_SERVER_FL_UPDATING, &server->flags)) { clear_bit(AFS_SERVER_FL_NEEDS_UPDATE, &server->flags); - success = afs_update_server_record(fc, server); + success = afs_update_server_record(op, server); clear_bit_unlock(AFS_SERVER_FL_UPDATING, &server->flags); wake_up_bit(&server->flags, AFS_SERVER_FL_UPDATING); _leave(" = %d", success); @@ -642,10 +647,10 @@ update: wait: ret = wait_on_bit(&server->flags, AFS_SERVER_FL_UPDATING, - (fc->flags & AFS_OPERATION_INTR) ? - TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + (op->flags & AFS_OPERATION_UNINTR) ? + TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); if (ret == -ERESTARTSYS) { - fc->error = ret; + op->error = ret; _leave(" = f [intr]"); return false; } diff --git a/fs/afs/super.c b/fs/afs/super.c index 9f412d7e7edf..c4bb314a22ae 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -373,7 +373,7 @@ static int afs_validate_fc(struct fs_context *fc) ctx->key = key; if (ctx->volume) { - afs_put_volume(ctx->cell, ctx->volume); + afs_put_volume(ctx->net, ctx->volume); ctx->volume = NULL; } @@ -421,7 +421,6 @@ 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_iget_data iget_data; struct inode *inode = NULL; int ret; @@ -446,13 +445,7 @@ 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); - 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); + inode = afs_root_iget(sb, ctx->key); } if (IS_ERR(inode)) @@ -496,7 +489,7 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc) as->dyn_root = true; } else { as->cell = afs_get_cell(ctx->cell); - as->volume = __afs_get_volume(ctx->volume); + as->volume = afs_get_volume(ctx->volume); } } return as; @@ -505,8 +498,9 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc) static void afs_destroy_sbi(struct afs_super_info *as) { if (as) { - afs_put_volume(as->cell, as->volume); - afs_put_cell(afs_net(as->net_ns), as->cell); + struct afs_net *net = afs_net(as->net_ns); + afs_put_volume(net, as->volume); + afs_put_cell(net, as->cell); put_net(as->net_ns); kfree(as); } @@ -592,7 +586,7 @@ static void afs_free_fc(struct fs_context *fc) struct afs_fs_context *ctx = fc->fs_private; afs_destroy_sbi(fc->s_fs_info); - afs_put_volume(ctx->cell, ctx->volume); + afs_put_volume(ctx->net, ctx->volume); afs_put_cell(ctx->net, ctx->cell); key_put(ctx->key); kfree(ctx); @@ -709,17 +703,32 @@ static void afs_destroy_inode(struct inode *inode) atomic_dec(&afs_count_active_inodes); } +static void afs_get_volume_status_success(struct afs_operation *op) +{ + struct afs_volume_status *vs = &op->volstatus.vs; + struct kstatfs *buf = op->volstatus.buf; + + if (vs->max_quota == 0) + buf->f_blocks = vs->part_max_blocks; + else + buf->f_blocks = vs->max_quota; + buf->f_bavail = buf->f_bfree = buf->f_blocks - vs->blocks_in_use; +} + +static const struct afs_operation_ops afs_get_volume_status_operation = { + .issue_afs_rpc = afs_fs_get_volume_status, + .issue_yfs_rpc = yfs_fs_get_volume_status, + .success = afs_get_volume_status_success, +}; + /* * return information about an AFS volume */ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct afs_super_info *as = AFS_FS_S(dentry->d_sb); - struct afs_operation fc; - struct afs_volume_status vs; + struct afs_operation *op; struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); - struct key *key; - int ret; buf->f_type = dentry->d_sb->s_magic; buf->f_bsize = AFS_BLOCK_SIZE; @@ -732,31 +741,13 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } - key = afs_request_key(vnode->volume->cell); - if (IS_ERR(key)) - return PTR_ERR(key); + op = afs_alloc_operation(NULL, as->volume); + if (IS_ERR(op)) + return PTR_ERR(op); - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, vnode, key, true)) { - fc.flags |= AFS_OPERATION_NO_VSLEEP; - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(vnode); - afs_fs_get_volume_status(&fc, &vs); - } - - afs_check_for_remote_deletion(&fc, fc.vnode); - ret = afs_end_vnode_operation(&fc); - } - - key_put(key); - - if (ret == 0) { - if (vs.max_quota == 0) - buf->f_blocks = vs.part_max_blocks; - else - buf->f_blocks = vs.max_quota; - buf->f_bavail = buf->f_bfree = buf->f_blocks - vs.blocks_in_use; - } - - return ret; + afs_op_set_vnode(op, 0, vnode); + op->nr_files = 1; + op->volstatus.buf = buf; + op->ops = &afs_get_volume_status_operation; + return afs_do_sync_operation(op); } diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 96351088a578..57d0509f7353 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -166,13 +166,13 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume) /* * Drop a reference on a volume record. */ -void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume) +void afs_put_volume(struct afs_net *net, struct afs_volume *volume) { if (volume) { _enter("%s", volume->name); if (atomic_dec_and_test(&volume->usage)) - afs_destroy_volume(cell->net, volume); + afs_destroy_volume(net, volume); } } @@ -280,7 +280,7 @@ error: /* * Make sure the volume record is up to date. */ -int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *fc) +int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op) { int ret, retries = 0; @@ -298,7 +298,7 @@ retry: update: if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) { clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags); - ret = afs_update_volume_status(volume, fc->key); + ret = afs_update_volume_status(volume, op->key); if (ret < 0) set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags); clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags); @@ -315,8 +315,8 @@ wait: } ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT, - (fc->flags & AFS_OPERATION_INTR) ? - TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + (op->flags & AFS_OPERATION_UNINTR) ? + TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); if (ret == -ERESTARTSYS) { _leave(" = %d", ret); return ret; diff --git a/fs/afs/write.c b/fs/afs/write.c index 1a8af44ea36b..97bccde3298b 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -349,82 +349,111 @@ static void afs_pages_written_back(struct afs_vnode *vnode, } /* - * write to a file + * Find a key to use for the writeback. We cached the keys used to author the + * writes on the vnode. *_wbk will contain the last writeback key used or NULL + * and we need to start from there if it's set. */ -static int afs_store_data(struct address_space *mapping, - pgoff_t first, pgoff_t last, - unsigned offset, unsigned to) +static int afs_get_writeback_key(struct afs_vnode *vnode, + struct afs_wb_key **_wbk) { - struct afs_vnode *vnode = AFS_FS_I(mapping->host); - struct afs_operation fc; - struct afs_status_cb *scb; struct afs_wb_key *wbk = NULL; struct list_head *p; int ret = -ENOKEY, ret2; - _enter("%s{%llx:%llu.%u},%lx,%lx,%x,%x", - vnode->volume->name, - vnode->fid.vid, - vnode->fid.vnode, - 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; + if (*_wbk) + p = (*_wbk)->vnode_link.next; + else + p = vnode->wb_keys.next; - /* Iterate through the list looking for a valid key to use. */ -try_next_key: while (p != &vnode->wb_keys) { wbk = list_entry(p, struct afs_wb_key, vnode_link); _debug("wbk %u", key_serial(wbk->key)); ret2 = key_validate(wbk->key); - if (ret2 == 0) - goto found_key; + if (ret2 == 0) { + refcount_inc(&wbk->usage); + _debug("USE WB KEY %u", key_serial(wbk->key)); + break; + } + + wbk = NULL; if (ret == -ENOKEY) ret = ret2; p = p->next; } spin_unlock(&vnode->wb_lock); - afs_put_wb_key(wbk); - kfree(scb); - _leave(" = %d [no keys]", ret); - return ret; + if (*_wbk) + afs_put_wb_key(*_wbk); + *_wbk = wbk; + return 0; +} -found_key: - refcount_inc(&wbk->usage); - spin_unlock(&vnode->wb_lock); +static void afs_store_data_success(struct afs_operation *op) +{ + struct afs_vnode *vnode = op->file[0].vnode; - _debug("USE WB KEY %u", key_serial(wbk->key)); + afs_vnode_commit_status(op, &op->file[0]); + if (op->error == 0) { + afs_pages_written_back(vnode, op->store.first, op->store.last); + afs_stat_v(vnode, n_stores); + atomic_long_add((op->store.last * PAGE_SIZE + op->store.last_to) - + (op->store.first * PAGE_SIZE + op->store.first_offset), + &afs_v2net(vnode)->n_store_bytes); + } +} - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, vnode, wbk->key, false)) { - afs_dataversion_t data_version = vnode->status.data_version + 1; +static const struct afs_operation_ops afs_store_data_operation = { + .issue_afs_rpc = afs_fs_store_data, + .issue_yfs_rpc = yfs_fs_store_data, + .success = afs_store_data_success, +}; - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(vnode); - afs_fs_store_data(&fc, mapping, first, last, offset, to, scb); - } +/* + * write to a file + */ +static int afs_store_data(struct address_space *mapping, + pgoff_t first, pgoff_t last, + unsigned offset, unsigned to) +{ + struct afs_vnode *vnode = AFS_FS_I(mapping->host); + struct afs_operation *op; + struct afs_wb_key *wbk = NULL; + int ret; - 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); + _enter("%s{%llx:%llu.%u},%lx,%lx,%x,%x", + vnode->volume->name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + first, last, offset, to); + + ret = afs_get_writeback_key(vnode, &wbk); + if (ret) { + _leave(" = %d [no keys]", ret); + return ret; } - switch (ret) { - case 0: - afs_stat_v(vnode, n_stores); - atomic_long_add((last * PAGE_SIZE + to) - - (first * PAGE_SIZE + offset), - &afs_v2net(vnode)->n_store_bytes); - break; + op = afs_alloc_operation(wbk->key, vnode->volume); + if (IS_ERR(op)) { + afs_put_wb_key(wbk); + return -ENOMEM; + } + + afs_op_set_vnode(op, 0, vnode); + op->file[0].dv_delta = 1; + op->store.mapping = mapping; + op->store.first = first; + op->store.last = last; + op->store.first_offset = offset; + op->store.last_to = to; + op->ops = &afs_store_data_operation; + +try_next_key: + afs_begin_vnode_operation(op); + afs_wait_for_operation(op); + + switch (op->error) { case -EACCES: case -EPERM: case -ENOKEY: @@ -432,16 +461,19 @@ found_key: case -EKEYREJECTED: case -EKEYREVOKED: _debug("next"); - spin_lock(&vnode->wb_lock); - p = wbk->vnode_link.next; - afs_put_wb_key(wbk); - goto try_next_key; + + ret = afs_get_writeback_key(vnode, &wbk); + if (ret == 0) { + key_put(op->key); + op->key = key_get(wbk->key); + goto try_next_key; + } + break; } afs_put_wb_key(wbk); - kfree(scb); - _leave(" = %d", ret); - return ret; + _leave(" = %d", op->error); + return afs_put_operation(op); } /* diff --git a/fs/afs/xattr.c b/fs/afs/xattr.c index bf645f1c90b0..84f3c4f57531 100644 --- a/fs/afs/xattr.c +++ b/fs/afs/xattr.c @@ -34,6 +34,25 @@ ssize_t afs_listxattr(struct dentry *dentry, char *buffer, size_t size) return sizeof(afs_xattr_list); } +/* + * Deal with the result of a successful fetch ACL operation. + */ +static void afs_acl_success(struct afs_operation *op) +{ + afs_vnode_commit_status(op, &op->file[0]); +} + +static void afs_acl_put(struct afs_operation *op) +{ + kfree(op->acl); +} + +static const struct afs_operation_ops afs_fetch_acl_operation = { + .issue_afs_rpc = afs_fs_fetch_acl, + .success = afs_acl_success, + .put = afs_acl_put, +}; + /* * Get a file's ACL. */ @@ -42,37 +61,23 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler, struct inode *inode, const char *name, void *buffer, size_t size) { - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_acl *acl = NULL; - struct key *key; - int ret = -ENOMEM; + int ret; - scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS); - if (!scb) - goto error; - - key = afs_request_key(vnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error_scb; - } + op = afs_alloc_operation(NULL, vnode->volume); + if (IS_ERR(op)) + 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); - acl = afs_fs_fetch_acl(&fc, scb); - } + afs_op_set_vnode(op, 0, vnode); + op->ops = &afs_fetch_acl_operation; - afs_check_for_remote_deletion(&fc, fc.vnode); - afs_vnode_commit_status(&fc, vnode, fc.cb_break, - &data_version, scb); - ret = afs_end_vnode_operation(&fc); - } + afs_begin_vnode_operation(op); + afs_wait_for_operation(op); + acl = op->acl; + op->acl = NULL; + ret = afs_put_operation(op); if (ret == 0) { ret = acl->size; @@ -80,18 +85,37 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler, if (acl->size <= size) memcpy(buffer, acl->data, acl->size); else - ret = -ERANGE; + op->error = -ERANGE; } - kfree(acl); } - key_put(key); -error_scb: - kfree(scb); -error: + kfree(acl); return ret; } +static bool afs_make_acl(struct afs_operation *op, + const void *buffer, size_t size) +{ + struct afs_acl *acl; + + acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL); + if (!acl) { + afs_op_nomem(op); + return false; + } + + acl->size = size; + memcpy(acl->data, buffer, size); + op->acl = acl; + return true; +} + +static const struct afs_operation_ops afs_store_acl_operation = { + .issue_afs_rpc = afs_fs_store_acl, + .success = afs_acl_success, + .put = afs_acl_put, +}; + /* * Set a file's AFS3 ACL. */ @@ -100,55 +124,22 @@ static int afs_xattr_set_acl(const struct xattr_handler *handler, struct inode *inode, const char *name, const void *buffer, size_t size, int flags) { - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *vnode = AFS_FS_I(inode); - struct afs_acl *acl = NULL; - struct key *key; - int ret = -ENOMEM; if (flags == XATTR_CREATE) return -EINVAL; - scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS); - if (!scb) - goto error; - - acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL); - 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; - memcpy(acl->data, buffer, size); - - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, vnode, key, true)) { - afs_dataversion_t data_version = vnode->status.data_version; + op = afs_alloc_operation(NULL, vnode->volume); + if (IS_ERR(op)) + return -ENOMEM; - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(vnode); - afs_fs_store_acl(&fc, acl, scb); - } + afs_op_set_vnode(op, 0, vnode); + if (!afs_make_acl(op, buffer, size)) + return afs_put_operation(op); - afs_check_for_remote_deletion(&fc, fc.vnode); - afs_vnode_commit_status(&fc, vnode, fc.cb_break, - &data_version, scb); - ret = afs_end_vnode_operation(&fc); - } - - key_put(key); -error_acl: - kfree(acl); -error_scb: - kfree(scb); -error: - return ret; + op->ops = &afs_store_acl_operation; + return afs_do_sync_operation(op); } static const struct xattr_handler afs_xattr_afs_acl_handler = { @@ -157,6 +148,17 @@ static const struct xattr_handler afs_xattr_afs_acl_handler = { .set = afs_xattr_set_acl, }; +static void yfs_acl_put(struct afs_operation *op) +{ + yfs_free_opaque_acl(op->yacl); +} + +static const struct afs_operation_ops yfs_fetch_opaque_acl_operation = { + .issue_yfs_rpc = yfs_fs_fetch_opaque_acl, + .success = afs_acl_success, + /* Don't free op->yacl in .put here */ +}; + /* * Get a file's YFS ACL. */ @@ -165,11 +167,9 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler, struct inode *inode, const char *name, void *buffer, size_t size) { - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *vnode = AFS_FS_I(inode); struct yfs_acl *yacl = NULL; - struct key *key; char buf[16], *data; int which = 0, dsize, ret = -ENOMEM; @@ -193,75 +193,62 @@ 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) + op = afs_alloc_operation(NULL, vnode->volume); + if (IS_ERR(op)) goto error_yacl; - key = afs_request_key(vnode->volume->cell); - if (IS_ERR(key)) { - ret = PTR_ERR(key); - goto error_scb; - } + afs_op_set_vnode(op, 0, vnode); + op->yacl = yacl; + op->ops = &yfs_fetch_opaque_acl_operation; - ret = -ERESTARTSYS; - if (afs_begin_vnode_operation(&fc, vnode, key, true)) { - afs_dataversion_t data_version = vnode->status.data_version; + afs_begin_vnode_operation(op); + afs_wait_for_operation(op); + ret = afs_put_operation(op); - while (afs_select_fileserver(&fc)) { - fc.cb_break = afs_calc_vnode_cb_break(vnode); - yfs_fs_fetch_opaque_acl(&fc, yacl, scb); + if (ret == 0) { + switch (which) { + case 0: + data = yacl->acl->data; + dsize = yacl->acl->size; + break; + case 1: + data = buf; + dsize = scnprintf(buf, sizeof(buf), "%u", yacl->inherit_flag); + break; + case 2: + data = buf; + dsize = scnprintf(buf, sizeof(buf), "%u", yacl->num_cleaned); + break; + case 3: + data = yacl->vol_acl->data; + dsize = yacl->vol_acl->size; + break; + default: + ret = -EOPNOTSUPP; + goto error_yacl; } - afs_check_for_remote_deletion(&fc, fc.vnode); - afs_vnode_commit_status(&fc, vnode, fc.cb_break, - &data_version, scb); - ret = afs_end_vnode_operation(&fc); - } - - if (ret < 0) - goto error_key; - - switch (which) { - case 0: - data = yacl->acl->data; - dsize = yacl->acl->size; - break; - case 1: - data = buf; - dsize = scnprintf(buf, sizeof(buf), "%u", yacl->inherit_flag); - break; - case 2: - data = buf; - dsize = scnprintf(buf, sizeof(buf), "%u", yacl->num_cleaned); - break; - case 3: - data = yacl->vol_acl->data; - dsize = yacl->vol_acl->size; - break; - default: - ret = -EOPNOTSUPP; - goto error_key; - } - - ret = dsize; - if (size > 0) { - if (dsize > size) { - ret = -ERANGE; - goto error_key; + ret = dsize; + if (size > 0) { + if (dsize <= size) + memcpy(buffer, data, dsize); + else + ret = -ERANGE; } - memcpy(buffer, data, dsize); } -error_key: - key_put(key); -error_scb: - kfree(scb); error_yacl: yfs_free_opaque_acl(yacl); error: return ret; } +static const struct afs_operation_ops yfs_store_opaque_acl2_operation = { + .issue_yfs_rpc = yfs_fs_store_opaque_acl2, + .success = afs_acl_success, + .put = yfs_acl_put, +}; + /* * Set a file's YFS ACL. */ @@ -270,56 +257,23 @@ static int afs_xattr_set_yfs(const struct xattr_handler *handler, struct inode *inode, const char *name, const void *buffer, size_t size, int flags) { - struct afs_operation fc; - struct afs_status_cb *scb; + struct afs_operation *op; struct afs_vnode *vnode = AFS_FS_I(inode); - struct afs_acl *acl = NULL; - struct key *key; - int ret = -ENOMEM; if (flags == XATTR_CREATE || strcmp(name, "acl") != 0) return -EINVAL; - scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS); - if (!scb) - goto error; - - acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL); - if (!acl) - goto error_scb; + op = afs_alloc_operation(NULL, vnode->volume); + if (IS_ERR(op)) + return -ENOMEM; - acl->size = size; - memcpy(acl->data, buffer, size); + afs_op_set_vnode(op, 0, vnode); + if (!afs_make_acl(op, buffer, size)) + return afs_put_operation(op); - 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, scb); - } - - afs_check_for_remote_deletion(&fc, fc.vnode); - 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; + op->ops = &yfs_store_opaque_acl2_operation; + return afs_do_sync_operation(op); } static const struct xattr_handler afs_xattr_yfs_handler = { diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index 360b4a560ba7..d0cd112a3720 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -17,11 +17,6 @@ static const struct afs_fid afs_zero_fid; -static inline void afs_use_fs_server(struct afs_call *call, struct afs_cb_interest *cbi) -{ - call->cbi = afs_get_cb_interest(cbi); -} - #define xdr_size(x) (sizeof(*x) / sizeof(__be32)) static void xdr_decode_YFSFid(const __be32 **_bp, struct afs_fid *fid) @@ -79,6 +74,11 @@ static __be32 *xdr_encode_string(__be32 *bp, const char *p, unsigned int len) return bp + len / sizeof(__be32); } +static __be32 *xdr_encode_name(__be32 *bp, const struct qstr *p) +{ + return xdr_encode_string(bp, p->name, p->len); +} + static s64 linux_to_yfs_time(const struct timespec64 *t) { /* Convert to 100ns intervals. */ @@ -336,6 +336,7 @@ static void xdr_decode_YFSFetchVolumeStatus(const __be32 **_bp, */ static int yfs_deliver_fs_status_cb_and_volsync(struct afs_call *call) { + struct afs_operation *op = call->op; const __be32 *bp; int ret; @@ -345,9 +346,9 @@ static int yfs_deliver_fs_status_cb_and_volsync(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_YFSCallBack(&bp, call, call->out_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFetchStatus(&bp, call, &op->file[0].scb); + xdr_decode_YFSCallBack(&bp, call, &op->file[0].scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -359,6 +360,7 @@ static int yfs_deliver_fs_status_cb_and_volsync(struct afs_call *call) */ static int yfs_deliver_status_and_volsync(struct afs_call *call) { + struct afs_operation *op = call->op; const __be32 *bp; int ret; @@ -367,8 +369,8 @@ static int yfs_deliver_status_and_volsync(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFetchStatus(&bp, call, &op->file[0].scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -387,44 +389,33 @@ static const struct afs_call_type yfs_RXYFSFetchStatus_vnode = { /* * Fetch the status information for a file. */ -int yfs_fs_fetch_file_status(struct afs_operation *fc, struct afs_status_cb *scb, - struct afs_volsync *volsync) +void yfs_fs_fetch_file_status(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call = afs_alloc_flat_call(net, &yfs_RXYFSFetchStatus_vnode, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSFetchStatus_vnode, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSCallBack) + sizeof(struct yfs_xdr_YFSVolSync)); - if (!call) { - fc->ac.error = -ENOMEM; - return -ENOMEM; - } - - call->key = fc->key; - call->out_scb = scb; - call->out_volsync = volsync; + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSFETCHSTATUS); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -432,7 +423,9 @@ int yfs_fs_fetch_file_status(struct afs_operation *fc, struct afs_status_cb *scb */ static int yfs_deliver_fs_fetch_data64(struct afs_call *call) { - struct afs_read *req = call->read_request; + struct afs_operation *op = call->op; + struct afs_vnode_param *vp = &op->file[0]; + struct afs_read *req = op->fetch.req; const __be32 *bp; unsigned int size; int ret; @@ -527,12 +520,12 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_YFSCallBack(&bp, call, call->out_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_YFSCallBack(&bp, call, &vp->scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); - req->data_version = call->out_scb->status.data_version; - req->file_size = call->out_scb->status.size; + req->data_version = vp->scb.status.data_version; + req->file_size = vp->scb.status.size; call->unmarshall++; /* Fall through */ @@ -556,12 +549,6 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call) return 0; } -static void yfs_fetch_data_destructor(struct afs_call *call) -{ - afs_put_read(call->read_request); - afs_flat_call_destructor(call); -} - /* * YFS.FetchData64 operation type */ @@ -569,25 +556,24 @@ static const struct afs_call_type yfs_RXYFSFetchData64 = { .name = "YFS.FetchData64", .op = yfs_FS_FetchData64, .deliver = yfs_deliver_fs_fetch_data64, - .destructor = yfs_fetch_data_destructor, + .destructor = afs_flat_call_destructor, }; /* * Fetch data from a file. */ -int yfs_fs_fetch_data(struct afs_operation *fc, struct afs_status_cb *scb, - struct afs_read *req) +void yfs_fs_fetch_data(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; + struct afs_read *req = op->fetch.req; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(",%x,{%llx:%llu},%llx,%llx", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode, + key_serial(op->key), vp->fid.vid, vp->fid.vnode, req->pos, req->len); - call = afs_alloc_flat_call(net, &yfs_RXYFSFetchData64, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSFetchData64, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid) + sizeof(struct yfs_xdr_u64) * 2, @@ -595,27 +581,19 @@ int yfs_fs_fetch_data(struct afs_operation *fc, struct afs_status_cb *scb, sizeof(struct yfs_xdr_YFSCallBack) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_scb = scb; - call->out_volsync = NULL; - call->read_request = afs_get_read(req); + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSFETCHDATA64); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); bp = xdr_encode_u64(bp, req->pos); bp = xdr_encode_u64(bp, req->len); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -623,6 +601,9 @@ int yfs_fs_fetch_data(struct afs_operation *fc, struct afs_status_cb *scb, */ static int yfs_deliver_fs_create_vnode(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; const __be32 *bp; int ret; @@ -634,11 +615,11 @@ static int yfs_deliver_fs_create_vnode(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_YFSFid(&bp, call->out_fid); - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_YFSCallBack(&bp, call, call->out_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFid(&bp, &op->file[1].fid); + xdr_decode_YFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_YFSFetchStatus(&bp, call, &dvp->scb); + xdr_decode_YFSCallBack(&bp, call, &vp->scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -657,26 +638,20 @@ static const struct afs_call_type afs_RXFSCreateFile = { /* * Create a file. */ -int yfs_fs_create_file(struct afs_operation *fc, - const char *name, - umode_t mode, - struct afs_status_cb *dvnode_scb, - struct afs_fid *newfid, - struct afs_status_cb *new_scb) +void yfs_fs_create_file(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(dvnode); - size_t namesz, reqsz, rplsz; + size_t reqsz, rplsz; __be32 *bp; _enter(""); - namesz = strlen(name); reqsz = (sizeof(__be32) + sizeof(__be32) + sizeof(struct yfs_xdr_YFSFid) + - xdr_strlen(namesz) + + xdr_strlen(name->len) + sizeof(struct yfs_xdr_YFSStoreStatus) + sizeof(__be32)); rplsz = (sizeof(struct yfs_xdr_YFSFid) + @@ -685,30 +660,22 @@ int yfs_fs_create_file(struct afs_operation *fc, sizeof(struct yfs_xdr_YFSCallBack) + sizeof(struct yfs_xdr_YFSVolSync)); - call = afs_alloc_flat_call(net, &afs_RXFSCreateFile, reqsz, rplsz); + call = afs_alloc_flat_call(op->net, &afs_RXFSCreateFile, reqsz, rplsz); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; - call->out_fid = newfid; - call->out_scb = new_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSCREATEFILE); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &dvnode->fid); - bp = xdr_encode_string(bp, name, namesz); - bp = xdr_encode_YFSStoreStatus_mode(bp, mode); + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_name(bp, name); + bp = xdr_encode_YFSStoreStatus_mode(bp, op->create.mode); bp = xdr_encode_u32(bp, yfs_LockNone); /* ViceLockType */ yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &dvnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } static const struct afs_call_type yfs_RXFSMakeDir = { @@ -721,26 +688,20 @@ static const struct afs_call_type yfs_RXFSMakeDir = { /* * Make a directory. */ -int yfs_fs_make_dir(struct afs_operation *fc, - const char *name, - umode_t mode, - struct afs_status_cb *dvnode_scb, - struct afs_fid *newfid, - struct afs_status_cb *new_scb) +void yfs_fs_make_dir(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(dvnode); - size_t namesz, reqsz, rplsz; + size_t reqsz, rplsz; __be32 *bp; _enter(""); - namesz = strlen(name); reqsz = (sizeof(__be32) + sizeof(struct yfs_xdr_RPCFlags) + sizeof(struct yfs_xdr_YFSFid) + - xdr_strlen(namesz) + + xdr_strlen(name->len) + sizeof(struct yfs_xdr_YFSStoreStatus)); rplsz = (sizeof(struct yfs_xdr_YFSFid) + sizeof(struct yfs_xdr_YFSFetchStatus) + @@ -748,29 +709,21 @@ int yfs_fs_make_dir(struct afs_operation *fc, sizeof(struct yfs_xdr_YFSCallBack) + sizeof(struct yfs_xdr_YFSVolSync)); - call = afs_alloc_flat_call(net, &yfs_RXFSMakeDir, reqsz, rplsz); + call = afs_alloc_flat_call(op->net, &yfs_RXFSMakeDir, reqsz, rplsz); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; - call->out_fid = newfid; - call->out_scb = new_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSMAKEDIR); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &dvnode->fid); - bp = xdr_encode_string(bp, name, namesz); - bp = xdr_encode_YFSStoreStatus_mode(bp, mode); + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_name(bp, name); + bp = xdr_encode_YFSStoreStatus_mode(bp, op->create.mode); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &dvnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -778,6 +731,9 @@ int yfs_fs_make_dir(struct afs_operation *fc, */ static int yfs_deliver_fs_remove_file2(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; struct afs_fid fid; const __be32 *bp; int ret; @@ -789,15 +745,24 @@ static int yfs_deliver_fs_remove_file2(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); + xdr_decode_YFSFetchStatus(&bp, call, &dvp->scb); xdr_decode_YFSFid(&bp, &fid); - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); + xdr_decode_YFSFetchStatus(&bp, call, &vp->scb); /* Was deleted if vnode->status.abort_code == VNOVNODE. */ - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSVolSync(&bp, &op->volsync); return 0; } +static void yfs_done_fs_remove_file2(struct afs_call *call) +{ + if (call->error == -ECONNABORTED && + call->abort_code == RX_INVALID_OPERATION) { + set_bit(AFS_SERVER_FL_NO_RM2, &call->server->flags); + call->op->flags |= AFS_OPERATION_DOWNGRADE; + } +} + /* * YFS.RemoveFile2 operation type. */ @@ -805,55 +770,44 @@ static const struct afs_call_type yfs_RXYFSRemoveFile2 = { .name = "YFS.RemoveFile2", .op = yfs_FS_RemoveFile2, .deliver = yfs_deliver_fs_remove_file2, + .done = yfs_done_fs_remove_file2, .destructor = afs_flat_call_destructor, }; /* * Remove a file and retrieve new file status. */ -int yfs_fs_remove_file2(struct afs_operation *fc, struct afs_vnode *vnode, - const char *name, struct afs_status_cb *dvnode_scb, - struct afs_status_cb *vnode_scb) +void yfs_fs_remove_file2(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + struct afs_vnode_param *dvp = &op->file[0]; + const struct qstr *name = &op->dentry->d_name; struct afs_call *call; - struct afs_net *net = afs_v2net(dvnode); - size_t namesz; __be32 *bp; _enter(""); - namesz = strlen(name); - - call = afs_alloc_flat_call(net, &yfs_RXYFSRemoveFile2, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSRemoveFile2, sizeof(__be32) + sizeof(struct yfs_xdr_RPCFlags) + sizeof(struct yfs_xdr_YFSFid) + - xdr_strlen(namesz), + xdr_strlen(name->len), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSFid) + sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; - call->out_scb = vnode_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSREMOVEFILE2); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &dvnode->fid); - bp = xdr_encode_string(bp, name, namesz); + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_name(bp, name); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &dvnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -861,6 +815,8 @@ int yfs_fs_remove_file2(struct afs_operation *fc, struct afs_vnode *vnode, */ static int yfs_deliver_fs_remove(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *dvp = &op->file[0]; const __be32 *bp; int ret; @@ -871,8 +827,8 @@ static int yfs_deliver_fs_remove(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFetchStatus(&bp, call, &dvp->scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); return 0; } @@ -886,6 +842,43 @@ static const struct afs_call_type yfs_RXYFSRemoveFile = { .destructor = afs_flat_call_destructor, }; +/* + * Remove a file. + */ +void yfs_fs_remove_file(struct afs_operation *op) +{ + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_call *call; + __be32 *bp; + + _enter(""); + + if (!test_bit(AFS_SERVER_FL_NO_RM2, &op->cbi->server->flags)) + return yfs_fs_remove_file2(op); + + call = afs_alloc_flat_call(op->net, &yfs_RXYFSRemoveFile, + sizeof(__be32) + + sizeof(struct yfs_xdr_RPCFlags) + + sizeof(struct yfs_xdr_YFSFid) + + xdr_strlen(name->len), + sizeof(struct yfs_xdr_YFSFetchStatus) + + sizeof(struct yfs_xdr_YFSVolSync)); + if (!call) + return afs_op_nomem(op); + + /* marshall the parameters */ + bp = call->request; + bp = xdr_encode_u32(bp, YFSREMOVEFILE); + bp = xdr_encode_u32(bp, 0); /* RPC flags */ + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_name(bp, name); + yfs_check_req(call, bp); + + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); +} + static const struct afs_call_type yfs_RXYFSRemoveDir = { .name = "YFS.RemoveDir", .op = yfs_FS_RemoveDir, @@ -894,48 +887,37 @@ static const struct afs_call_type yfs_RXYFSRemoveDir = { }; /* - * remove a file or directory + * Remove a directory. */ -int yfs_fs_remove(struct afs_operation *fc, struct afs_vnode *vnode, - const char *name, bool isdir, - struct afs_status_cb *dvnode_scb) +void yfs_fs_remove_dir(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(dvnode); - size_t namesz; __be32 *bp; _enter(""); - namesz = strlen(name); - call = afs_alloc_flat_call( - net, isdir ? &yfs_RXYFSRemoveDir : &yfs_RXYFSRemoveFile, - sizeof(__be32) + - sizeof(struct yfs_xdr_RPCFlags) + - sizeof(struct yfs_xdr_YFSFid) + - xdr_strlen(namesz), - sizeof(struct yfs_xdr_YFSFetchStatus) + - sizeof(struct yfs_xdr_YFSVolSync)); + call = afs_alloc_flat_call(op->net, &yfs_RXYFSRemoveDir, + sizeof(__be32) + + sizeof(struct yfs_xdr_RPCFlags) + + sizeof(struct yfs_xdr_YFSFid) + + xdr_strlen(name->len), + sizeof(struct yfs_xdr_YFSFetchStatus) + + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; - bp = xdr_encode_u32(bp, isdir ? YFSREMOVEDIR : YFSREMOVEFILE); + bp = xdr_encode_u32(bp, YFSREMOVEDIR); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &dvnode->fid); - bp = xdr_encode_string(bp, name, namesz); + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_name(bp, name); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &dvnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -943,6 +925,9 @@ int yfs_fs_remove(struct afs_operation *fc, struct afs_vnode *vnode, */ static int yfs_deliver_fs_link(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; const __be32 *bp; int ret; @@ -953,9 +938,9 @@ static int yfs_deliver_fs_link(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_YFSFetchStatus(&bp, call, &dvp->scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; } @@ -973,50 +958,39 @@ static const struct afs_call_type yfs_RXYFSLink = { /* * Make a hard link. */ -int yfs_fs_link(struct afs_operation *fc, struct afs_vnode *vnode, - const char *name, - struct afs_status_cb *dvnode_scb, - struct afs_status_cb *vnode_scb) +void yfs_fs_link(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); - size_t namesz; __be32 *bp; _enter(""); - namesz = strlen(name); - call = afs_alloc_flat_call(net, &yfs_RXYFSLink, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSLink, sizeof(__be32) + sizeof(struct yfs_xdr_RPCFlags) + sizeof(struct yfs_xdr_YFSFid) + - xdr_strlen(namesz) + + xdr_strlen(name->len) + sizeof(struct yfs_xdr_YFSFid), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; - call->out_scb = vnode_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSLINK); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &dvnode->fid); - bp = xdr_encode_string(bp, name, namesz); - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_name(bp, name); + bp = xdr_encode_YFSFid(bp, &vp->fid); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &vnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &vp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1024,6 +998,9 @@ int yfs_fs_link(struct afs_operation *fc, struct afs_vnode *vnode, */ static int yfs_deliver_fs_symlink(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; const __be32 *bp; int ret; @@ -1035,10 +1012,10 @@ static int yfs_deliver_fs_symlink(struct afs_call *call) /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_YFSFid(&bp, call->out_fid); - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFid(&bp, &vp->fid); + xdr_decode_YFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_YFSFetchStatus(&bp, call, &dvp->scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; @@ -1057,28 +1034,22 @@ static const struct afs_call_type yfs_RXYFSSymlink = { /* * Create a symbolic link. */ -int yfs_fs_symlink(struct afs_operation *fc, - const char *name, - const char *contents, - struct afs_status_cb *dvnode_scb, - struct afs_fid *newfid, - struct afs_status_cb *vnode_scb) +void yfs_fs_symlink(struct afs_operation *op) { - struct afs_vnode *dvnode = fc->vnode; + const struct qstr *name = &op->dentry->d_name; + struct afs_vnode_param *dvp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(dvnode); - size_t namesz, contents_sz; + size_t contents_sz; __be32 *bp; _enter(""); - namesz = strlen(name); - contents_sz = strlen(contents); - call = afs_alloc_flat_call(net, &yfs_RXYFSSymlink, + contents_sz = strlen(op->create.symlink); + call = afs_alloc_flat_call(op->net, &yfs_RXYFSSymlink, sizeof(__be32) + sizeof(struct yfs_xdr_RPCFlags) + sizeof(struct yfs_xdr_YFSFid) + - xdr_strlen(namesz) + + xdr_strlen(name->len) + xdr_strlen(contents_sz) + sizeof(struct yfs_xdr_YFSStoreStatus), sizeof(struct yfs_xdr_YFSFid) + @@ -1086,28 +1057,20 @@ int yfs_fs_symlink(struct afs_operation *fc, sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = dvnode_scb; - call->out_fid = newfid; - call->out_scb = vnode_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSSYMLINK); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &dvnode->fid); - bp = xdr_encode_string(bp, name, namesz); - bp = xdr_encode_string(bp, contents, contents_sz); + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_name(bp, name); + bp = xdr_encode_string(bp, op->create.symlink, contents_sz); bp = xdr_encode_YFSStoreStatus_mode(bp, S_IRWXUGO); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call1(call, &dvnode->fid, name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call1(call, &dvp->fid, name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1115,6 +1078,9 @@ int yfs_fs_symlink(struct afs_operation *fc, */ static int yfs_deliver_fs_rename(struct afs_call *call) { + struct afs_operation *op = call->op; + struct afs_vnode_param *orig_dvp = &op->file[0]; + struct afs_vnode_param *new_dvp = &op->file[1]; const __be32 *bp; int ret; @@ -1128,9 +1094,9 @@ static int yfs_deliver_fs_rename(struct afs_call *call) /* If the two dirs are the same, we have two copies of the same status * report, so we just decode it twice. */ - xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFetchStatus(&bp, call, &orig_dvp->scb); + xdr_decode_YFSFetchStatus(&bp, call, &new_dvp->scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); _leave(" = 0 [done]"); return 0; } @@ -1148,55 +1114,42 @@ static const struct afs_call_type yfs_RXYFSRename = { /* * Rename a file or directory. */ -int yfs_fs_rename(struct afs_operation *fc, - const char *orig_name, - struct afs_vnode *new_dvnode, - const char *new_name, - struct afs_status_cb *orig_dvnode_scb, - struct afs_status_cb *new_dvnode_scb) +void yfs_fs_rename(struct afs_operation *op) { - struct afs_vnode *orig_dvnode = fc->vnode; + struct afs_vnode_param *orig_dvp = &op->file[0]; + struct afs_vnode_param *new_dvp = &op->file[1]; + const struct qstr *orig_name = &op->dentry->d_name; + const struct qstr *new_name = &op->dentry_2->d_name; struct afs_call *call; - struct afs_net *net = afs_v2net(orig_dvnode); - size_t o_namesz, n_namesz; __be32 *bp; _enter(""); - o_namesz = strlen(orig_name); - n_namesz = strlen(new_name); - call = afs_alloc_flat_call(net, &yfs_RXYFSRename, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSRename, sizeof(__be32) + sizeof(struct yfs_xdr_RPCFlags) + sizeof(struct yfs_xdr_YFSFid) + - xdr_strlen(o_namesz) + + xdr_strlen(orig_name->len) + sizeof(struct yfs_xdr_YFSFid) + - xdr_strlen(n_namesz), + xdr_strlen(new_name->len), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_dir_scb = orig_dvnode_scb; - call->out_scb = new_dvnode_scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSRENAME); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &orig_dvnode->fid); - bp = xdr_encode_string(bp, orig_name, o_namesz); - bp = xdr_encode_YFSFid(bp, &new_dvnode->fid); - bp = xdr_encode_string(bp, new_name, n_namesz); + bp = xdr_encode_YFSFid(bp, &orig_dvp->fid); + bp = xdr_encode_name(bp, orig_name); + bp = xdr_encode_YFSFid(bp, &new_dvp->fid); + bp = xdr_encode_name(bp, new_name); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call2(call, &orig_dvnode->fid, orig_name, new_name); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call2(call, &orig_dvp->fid, orig_name, new_name); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1212,27 +1165,23 @@ static const struct afs_call_type yfs_RXYFSStoreData64 = { /* * Store a set of pages to a large file. */ -int yfs_fs_store_data(struct afs_operation *fc, struct address_space *mapping, - pgoff_t first, pgoff_t last, - unsigned offset, unsigned to, - struct afs_status_cb *scb) +void yfs_fs_store_data(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); loff_t size, pos, i_size; __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - size = (loff_t)to - (loff_t)offset; - if (first != last) - size += (loff_t)(last - first) << PAGE_SHIFT; - pos = (loff_t)first << PAGE_SHIFT; - pos += offset; + size = (loff_t)op->store.last_to - (loff_t)op->store.first_offset; + if (op->store.first != op->store.last) + size += (loff_t)(op->store.last - op->store.first) << PAGE_SHIFT; + pos = (loff_t)op->store.first << PAGE_SHIFT; + pos += op->store.first_offset; - i_size = i_size_read(&vnode->vfs_inode); + i_size = i_size_read(&vp->vnode->vfs_inode); if (pos + size > i_size) i_size = size + pos; @@ -1240,7 +1189,7 @@ int yfs_fs_store_data(struct afs_operation *fc, struct address_space *mapping, (unsigned long long)size, (unsigned long long)pos, (unsigned long long)i_size); - call = afs_alloc_flat_call(net, &yfs_RXYFSStoreData64, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSStoreData64, sizeof(__be32) + sizeof(__be32) + sizeof(struct yfs_xdr_YFSFid) + @@ -1249,33 +1198,24 @@ int yfs_fs_store_data(struct afs_operation *fc, struct address_space *mapping, sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->mapping = mapping; - call->first = first; - call->last = last; - call->first_offset = offset; - call->last_to = to; + return afs_op_nomem(op); + + call->key = op->key; call->send_pages = true; - call->out_scb = scb; /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSSTOREDATA64); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); - bp = xdr_encode_YFSStoreStatus_mtime(bp, &vnode->vfs_inode.i_mtime); + bp = xdr_encode_YFSFid(bp, &vp->fid); + bp = xdr_encode_YFSStoreStatus_mtime(bp, &op->mtime); bp = xdr_encode_u64(bp, pos); bp = xdr_encode_u64(bp, size); bp = xdr_encode_u64(bp, i_size); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1299,18 +1239,17 @@ static const struct afs_call_type yfs_RXYFSStoreData64_as_Status = { * Set the attributes on a file, using YFS.StoreData64 rather than * YFS.StoreStatus so as to alter the file size also. */ -static int yfs_fs_setattr_size(struct afs_operation *fc, struct iattr *attr, - struct afs_status_cb *scb) +static void yfs_fs_setattr_size(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); + struct iattr *attr = op->setattr.attr; __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call = afs_alloc_flat_call(net, &yfs_RXYFSStoreData64_as_Status, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSStoreData64_as_Status, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid) + sizeof(struct yfs_xdr_YFSStoreStatus) + @@ -1318,72 +1257,59 @@ static int yfs_fs_setattr_size(struct afs_operation *fc, struct iattr *attr, sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSSTOREDATA64); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); bp = xdr_encode_YFS_StoreStatus(bp, attr); bp = xdr_encode_u64(bp, attr->ia_size); /* position of start of write */ bp = xdr_encode_u64(bp, 0); /* size of write */ bp = xdr_encode_u64(bp, attr->ia_size); /* new file length */ yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* * Set the attributes on a file, using YFS.StoreData64 if there's a change in * file size, and YFS.StoreStatus otherwise. */ -int yfs_fs_setattr(struct afs_operation *fc, struct iattr *attr, - struct afs_status_cb *scb) +void yfs_fs_setattr(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); + struct iattr *attr = op->setattr.attr; __be32 *bp; if (attr->ia_valid & ATTR_SIZE) - return yfs_fs_setattr_size(fc, attr, scb); + return yfs_fs_setattr_size(op); _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call = afs_alloc_flat_call(net, &yfs_RXYFSStoreStatus, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSStoreStatus, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid) + sizeof(struct yfs_xdr_YFSStoreStatus), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSSTORESTATUS); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); bp = xdr_encode_YFS_StoreStatus(bp, attr); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1391,6 +1317,7 @@ int yfs_fs_setattr(struct afs_operation *fc, struct iattr *attr, */ static int yfs_deliver_fs_get_volume_status(struct afs_call *call) { + struct afs_operation *op = call->op; const __be32 *bp; char *p; u32 size; @@ -1412,7 +1339,7 @@ static int yfs_deliver_fs_get_volume_status(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_YFSFetchVolumeStatus(&bp, call->out_volstatus); + xdr_decode_YFSFetchVolumeStatus(&bp, &op->volstatus.vs); call->unmarshall++; afs_extract_to_tmp(call); /* Fall through */ @@ -1526,17 +1453,15 @@ static const struct afs_call_type yfs_RXYFSGetVolumeStatus = { /* * fetch the status of a volume */ -int yfs_fs_get_volume_status(struct afs_operation *fc, - struct afs_volume_status *vs) +void yfs_fs_get_volume_status(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(""); - call = afs_alloc_flat_call(net, &yfs_RXYFSGetVolumeStatus, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSGetVolumeStatus, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_u64), max_t(size_t, @@ -1544,23 +1469,17 @@ int yfs_fs_get_volume_status(struct afs_operation *fc, sizeof(__be32), AFSOPAQUEMAX + 1)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->out_volstatus = vs; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSGETVOLUMESTATUS); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_u64(bp, vnode->fid.vid); + bp = xdr_encode_u64(bp, vp->fid.vid); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1598,118 +1517,93 @@ static const struct afs_call_type yfs_RXYFSReleaseLock = { /* * Set a lock on a file */ -int yfs_fs_set_lock(struct afs_operation *fc, afs_lock_type_t type, - struct afs_status_cb *scb) +void yfs_fs_set_lock(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(""); - call = afs_alloc_flat_call(net, &yfs_RXYFSSetLock, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSSetLock, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid) + sizeof(__be32), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->lvnode = vnode; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSSETLOCK); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); - bp = xdr_encode_u32(bp, type); + bp = xdr_encode_YFSFid(bp, &vp->fid); + bp = xdr_encode_u32(bp, op->lock.type); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_calli(call, &vnode->fid, type); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_calli(call, &vp->fid, op->lock.type); + afs_make_op_call(op, call, GFP_NOFS); } /* * extend a lock on a file */ -int yfs_fs_extend_lock(struct afs_operation *fc, struct afs_status_cb *scb) +void yfs_fs_extend_lock(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(""); - call = afs_alloc_flat_call(net, &yfs_RXYFSExtendLock, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSExtendLock, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->lvnode = vnode; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSEXTENDLOCK); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* * release a lock on a file */ -int yfs_fs_release_lock(struct afs_operation *fc, struct afs_status_cb *scb) +void yfs_fs_release_lock(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(""); - call = afs_alloc_flat_call(net, &yfs_RXYFSReleaseLock, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSReleaseLock, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); if (!call) - return -ENOMEM; - - call->key = fc->key; - call->lvnode = vnode; - call->out_scb = scb; + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSRELEASELOCK); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1725,45 +1619,33 @@ static const struct afs_call_type yfs_RXYFSFetchStatus = { /* * Fetch the status information for a fid without needing a vnode handle. */ -int yfs_fs_fetch_status(struct afs_operation *fc, - struct afs_net *net, - struct afs_fid *fid, - struct afs_status_cb *scb, - struct afs_volsync *volsync) +void yfs_fs_fetch_status(struct afs_operation *op) { + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), fid->vid, fid->vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call = afs_alloc_flat_call(net, &yfs_RXYFSFetchStatus, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSFetchStatus, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid), sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSCallBack) + sizeof(struct yfs_xdr_YFSVolSync)); - if (!call) { - fc->ac.error = -ENOMEM; - return -ENOMEM; - } - - call->key = fc->key; - call->out_scb = scb; - call->out_volsync = volsync; + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSFETCHSTATUS); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, fid); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1771,6 +1653,7 @@ int yfs_fs_fetch_status(struct afs_operation *fc, */ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) { + struct afs_operation *op = call->op; struct afs_status_cb *scb; const __be32 *bp; u32 tmp; @@ -1792,8 +1675,8 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) return ret; tmp = ntohl(call->tmp); - _debug("status count: %u/%u", tmp, call->count2); - if (tmp != call->count2) + _debug("status count: %u/%u", tmp, op->nr_files); + if (tmp != op->nr_files) return afs_protocol_error(call, afs_eproto_ibulkst_count); call->count = 0; @@ -1808,12 +1691,23 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) if (ret < 0) return ret; + switch (call->count) { + case 0: + scb = &op->file[0].scb; + break; + case 1: + scb = &op->file[1].scb; + break; + default: + scb = &op->more_files[call->count - 2].scb; + break; + } + bp = call->buffer; - scb = &call->out_scb[call->count]; xdr_decode_YFSFetchStatus(&bp, call, scb); call->count++; - if (call->count < call->count2) + if (call->count < op->nr_files) goto more_counts; call->count = 0; @@ -1830,7 +1724,7 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) tmp = ntohl(call->tmp); _debug("CB count: %u", tmp); - if (tmp != call->count2) + if (tmp != op->nr_files) return afs_protocol_error(call, afs_eproto_ibulkst_cb_count); call->count = 0; call->unmarshall++; @@ -1845,11 +1739,22 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) return ret; _debug("unmarshall CB array"); + switch (call->count) { + case 0: + scb = &op->file[0].scb; + break; + case 1: + scb = &op->file[1].scb; + break; + default: + scb = &op->more_files[call->count - 2].scb; + break; + } + bp = call->buffer; - scb = &call->out_scb[call->count]; xdr_decode_YFSCallBack(&bp, call, scb); call->count++; - if (call->count < call->count2) + if (call->count < op->nr_files) goto more_cbs; afs_extract_to_buf(call, sizeof(struct yfs_xdr_YFSVolSync)); @@ -1862,7 +1767,7 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call) return ret; bp = call->buffer; - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSVolSync(&bp, &op->volsync); call->unmarshall++; /* Fall through */ @@ -1888,50 +1793,39 @@ static const struct afs_call_type yfs_RXYFSInlineBulkStatus = { /* * Fetch the status information for up to 1024 files */ -int yfs_fs_inline_bulk_status(struct afs_operation *fc, - struct afs_net *net, - struct afs_fid *fids, - struct afs_status_cb *statuses, - unsigned int nr_fids, - struct afs_volsync *volsync) +void yfs_fs_inline_bulk_status(struct afs_operation *op) { + struct afs_vnode_param *dvp = &op->file[0]; + struct afs_vnode_param *vp = &op->file[1]; struct afs_call *call; __be32 *bp; int i; _enter(",%x,{%llx:%llu},%u", - key_serial(fc->key), fids[0].vid, fids[1].vnode, nr_fids); + key_serial(op->key), vp->fid.vid, vp->fid.vnode, op->nr_files); - call = afs_alloc_flat_call(net, &yfs_RXYFSInlineBulkStatus, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSInlineBulkStatus, sizeof(__be32) + sizeof(__be32) + sizeof(__be32) + - sizeof(struct yfs_xdr_YFSFid) * nr_fids, + sizeof(struct yfs_xdr_YFSFid) * op->nr_files, sizeof(struct yfs_xdr_YFSFetchStatus)); - if (!call) { - fc->ac.error = -ENOMEM; - return -ENOMEM; - } - - call->key = fc->key; - call->out_scb = statuses; - call->out_volsync = volsync; - call->count2 = nr_fids; + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSINLINEBULKSTATUS); bp = xdr_encode_u32(bp, 0); /* RPCFlags */ - bp = xdr_encode_u32(bp, nr_fids); - for (i = 0; i < nr_fids; i++) - bp = xdr_encode_YFSFid(bp, &fids[i]); + bp = xdr_encode_u32(bp, op->nr_files); + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); + for (i = 0; i < op->nr_files - 2; i++) + bp = xdr_encode_YFSFid(bp, &op->more_files[i].fid); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &fids[0]); - afs_set_fc_call(call, fc); - afs_make_call(&fc->ac, call, GFP_NOFS); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_NOFS); } /* @@ -1939,7 +1833,9 @@ int yfs_fs_inline_bulk_status(struct afs_operation *fc, */ static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call) { - struct yfs_acl *yacl = call->out_yacl; + struct afs_operation *op = call->op; + struct afs_vnode_param *vp = &op->file[0]; + struct yfs_acl *yacl = op->yacl; struct afs_acl *acl; const __be32 *bp; unsigned int size; @@ -2029,8 +1925,8 @@ static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call) bp = call->buffer; yacl->inherit_flag = ntohl(*bp++); yacl->num_cleaned = ntohl(*bp++); - xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); - xdr_decode_YFSVolSync(&bp, call->out_volsync); + xdr_decode_YFSFetchStatus(&bp, call, &vp->scb); + xdr_decode_YFSVolSync(&bp, &op->volsync); call->unmarshall++; /* Fall through */ @@ -2065,45 +1961,33 @@ static const struct afs_call_type yfs_RXYFSFetchOpaqueACL = { /* * Fetch the YFS advanced ACLs for a file. */ -struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_operation *fc, - struct yfs_acl *yacl, - struct afs_status_cb *scb) +void yfs_fs_fetch_opaque_acl(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); - call = afs_alloc_flat_call(net, &yfs_RXYFSFetchOpaqueACL, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSFetchOpaqueACL, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid), sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); - if (!call) { - fc->ac.error = -ENOMEM; - return ERR_PTR(-ENOMEM); - } - - call->key = fc->key; - call->out_yacl = yacl; - call->out_scb = scb; - call->out_volsync = NULL; + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSFETCHOPAQUEACL); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); yfs_check_req(call, bp); - afs_use_fs_server(call, fc->cbi); - trace_afs_make_fs_call(call, &vnode->fid); - afs_make_call(&fc->ac, call, GFP_KERNEL); - return (struct yfs_acl *)afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_KERNEL); } /* @@ -2119,46 +2003,38 @@ static const struct afs_call_type yfs_RXYFSStoreOpaqueACL2 = { /* * Fetch the YFS ACL for a file. */ -int yfs_fs_store_opaque_acl2(struct afs_operation *fc, const struct afs_acl *acl, - struct afs_status_cb *scb) +void yfs_fs_store_opaque_acl2(struct afs_operation *op) { - struct afs_vnode *vnode = fc->vnode; + struct afs_vnode_param *vp = &op->file[0]; struct afs_call *call; - struct afs_net *net = afs_v2net(vnode); + struct afs_acl *acl = op->acl; size_t size; __be32 *bp; _enter(",%x,{%llx:%llu},,", - key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); + key_serial(op->key), vp->fid.vid, vp->fid.vnode); size = round_up(acl->size, 4); - call = afs_alloc_flat_call(net, &yfs_RXYFSStoreOpaqueACL2, + call = afs_alloc_flat_call(op->net, &yfs_RXYFSStoreOpaqueACL2, sizeof(__be32) * 2 + sizeof(struct yfs_xdr_YFSFid) + sizeof(__be32) + size, sizeof(struct yfs_xdr_YFSFetchStatus) + sizeof(struct yfs_xdr_YFSVolSync)); - if (!call) { - fc->ac.error = -ENOMEM; - return -ENOMEM; - } - - call->key = fc->key; - call->out_scb = scb; - call->out_volsync = NULL; + if (!call) + return afs_op_nomem(op); /* marshall the parameters */ bp = call->request; bp = xdr_encode_u32(bp, YFSSTOREOPAQUEACL2); bp = xdr_encode_u32(bp, 0); /* RPC flags */ - bp = xdr_encode_YFSFid(bp, &vnode->fid); + bp = xdr_encode_YFSFid(bp, &vp->fid); bp = xdr_encode_u32(bp, acl->size); memcpy(bp, acl->data, acl->size); if (acl->size != size) memset((void *)bp + acl->size, 0, size - acl->size); yfs_check_req(call, bp); - trace_afs_make_fs_call(call, &vnode->fid); - afs_make_call(&fc->ac, call, GFP_KERNEL); - return afs_wait_for_call_to_complete(call, &fc->ac); + trace_afs_make_fs_call(call, &vp->fid); + afs_make_op_call(op, call, GFP_KERNEL); } diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index a6d8a9891164..f4d66919fb22 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -642,7 +642,7 @@ TRACE_EVENT(afs_make_fs_calli, TRACE_EVENT(afs_make_fs_call1, TP_PROTO(struct afs_call *call, const struct afs_fid *fid, - const char *name), + const struct qstr *name), TP_ARGS(call, fid, name), @@ -654,8 +654,7 @@ TRACE_EVENT(afs_make_fs_call1, ), TP_fast_assign( - int __len = strlen(name); - __len = min(__len, 23); + unsigned int __len = min_t(unsigned int, name->len, 23); __entry->call = call->debug_id; __entry->op = call->operation_ID; if (fid) { @@ -665,7 +664,7 @@ TRACE_EVENT(afs_make_fs_call1, __entry->fid.vnode = 0; __entry->fid.unique = 0; } - memcpy(__entry->name, name, __len); + memcpy(__entry->name, name->name, __len); __entry->name[__len] = 0; ), @@ -680,7 +679,7 @@ TRACE_EVENT(afs_make_fs_call1, TRACE_EVENT(afs_make_fs_call2, TP_PROTO(struct afs_call *call, const struct afs_fid *fid, - const char *name, const char *name2), + const struct qstr *name, const struct qstr *name2), TP_ARGS(call, fid, name, name2), @@ -693,10 +692,8 @@ TRACE_EVENT(afs_make_fs_call2, ), TP_fast_assign( - int __len = strlen(name); - int __len2 = strlen(name2); - __len = min(__len, 23); - __len2 = min(__len2, 23); + unsigned int __len = min_t(unsigned int, name->len, 23); + unsigned int __len2 = min_t(unsigned int, name2->len, 23); __entry->call = call->debug_id; __entry->op = call->operation_ID; if (fid) { @@ -706,9 +703,9 @@ TRACE_EVENT(afs_make_fs_call2, __entry->fid.vnode = 0; __entry->fid.unique = 0; } - memcpy(__entry->name, name, __len); + memcpy(__entry->name, name->name, __len); __entry->name[__len] = 0; - memcpy(__entry->name2, name2, __len2); + memcpy(__entry->name2, name2->name, __len2); __entry->name2[__len2] = 0; ), -- cgit v1.2.3-70-g09d2 From 44746355ccb142341f92a0c86fc2e27bfc968b40 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 27 May 2020 15:52:02 +0100 Subject: afs: Don't get epoch from a server because it may be ambiguous Don't get the epoch from a server, particularly one that we're looking up by UUID, as UUIDs may be ambiguous and may map to more than one server - so we can't draw any conclusions from it. Reported-by: Jeffrey Altman Signed-off-by: David Howells --- fs/afs/cmservice.c | 49 ++----------------------------------------------- fs/afs/internal.h | 7 ------- 2 files changed, 2 insertions(+), 54 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index ed0fb34d77dd..954030ae7a0f 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -118,8 +118,6 @@ bool afs_cm_incoming_call(struct afs_call *call) { _enter("{%u, CB.OP %u}", call->service_id, call->operation_ID); - call->epoch = rxrpc_kernel_get_epoch(call->net->socket, call->rxcall); - switch (call->operation_ID) { case CBCallBack: call->type = &afs_SRXCBCallBack; @@ -149,49 +147,6 @@ bool afs_cm_incoming_call(struct afs_call *call) } } -/* - * Record a probe to the cache manager from a server. - */ -static int afs_record_cm_probe(struct afs_call *call, struct afs_server *server) -{ - _enter(""); - - if (test_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags) && - !afs_is_probing_server(server)) { - if (server->cm_epoch == call->epoch) - return 0; - - if (!server->probe.said_rebooted) { - pr_notice("kAFS: FS rebooted %pU\n", &server->uuid); - server->probe.said_rebooted = true; - } - } - - spin_lock(&server->probe_lock); - - if (!test_and_set_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags)) { - server->cm_epoch = call->epoch; - server->probe.cm_epoch = call->epoch; - goto out; - } - - if (server->probe.cm_probed && - call->epoch != server->probe.cm_epoch && - !server->probe.said_inconsistent) { - pr_notice("kAFS: FS endpoints inconsistent %pU\n", - &server->uuid); - server->probe.said_inconsistent = true; - } - - if (!server->probe.cm_probed || call->epoch == server->cm_epoch) - server->probe.cm_epoch = server->cm_epoch; - -out: - server->probe.cm_probed = true; - spin_unlock(&server->probe_lock); - return 0; -} - /* * Find the server record by peer address and record a probe to the cache * manager from a server. @@ -210,7 +165,7 @@ static int afs_find_cm_server_by_peer(struct afs_call *call) } call->server = server; - return afs_record_cm_probe(call, server); + return 0; } /* @@ -231,7 +186,7 @@ static int afs_find_cm_server_by_uuid(struct afs_call *call, } call->server = server; - return afs_record_cm_probe(call, server); + return 0; } /* diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 4b8ac049fc17..9f024c1bd650 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -124,7 +124,6 @@ struct afs_call { spinlock_t state_lock; int error; /* error code */ u32 abort_code; /* Remote abort ID or 0 */ - u32 epoch; unsigned int max_lifespan; /* Maximum lifespan to set if not 0 */ unsigned request_size; /* size of request data */ unsigned reply_max; /* maximum size of reply */ @@ -491,12 +490,10 @@ struct afs_server { #define AFS_SERVER_FL_MAY_HAVE_CB 8 /* May have callbacks on this fileserver */ #define AFS_SERVER_FL_IS_YFS 9 /* Server is YFS not AFS */ #define AFS_SERVER_FL_NO_RM2 10 /* Fileserver doesn't support YFS.RemoveFile2 */ -#define AFS_SERVER_FL_HAVE_EPOCH 11 /* ->epoch is valid */ #define AFS_SERVER_FL_NEEDS_UPDATE 12 /* Fileserver address list is out of date */ atomic_t ref; /* Object refcount */ atomic_t active; /* Active user count */ u32 addr_version; /* Address list version */ - u32 cm_epoch; /* Server RxRPC epoch */ unsigned int debug_id; /* Debugging ID for traces */ /* file service access */ @@ -515,15 +512,11 @@ struct afs_server { struct { unsigned int rtt; /* RTT as ktime/64 */ u32 abort_code; - u32 cm_epoch; short error; bool responded:1; bool is_yfs:1; bool not_yfs:1; bool local_failure:1; - bool cm_probed:1; - bool said_rebooted:1; - bool said_inconsistent:1; } probe; }; -- cgit v1.2.3-70-g09d2 From 3120c170ef35c1a9be137535e44c7e237e33b78e Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 27 May 2020 16:44:02 +0100 Subject: afs: Fix handling of CB.ProbeUuid cache manager op The AFS filesystem driver is handling the CB.ProbeUuid request incorrectly. The UUID presented in the request is that of the cache manager, not the fileserver, so afs_deliver_cb_probe_uuid() shouldn't be using that UUID to look up the server. Fix this by looking up the server by address instead. Signed-off-by: David Howells --- fs/afs/cmservice.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 954030ae7a0f..bef413818af7 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -464,7 +464,8 @@ static int afs_deliver_cb_probe(struct afs_call *call) } /* - * allow the fileserver to quickly find out if the fileserver has been rebooted + * Allow the fileserver to quickly find out if the cache manager has been + * rebooted. */ static void SRXAFSCB_ProbeUuid(struct work_struct *work) { @@ -536,7 +537,7 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call) if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) return afs_io_error(call, afs_io_error_cm_reply); - return afs_find_cm_server_by_uuid(call, call->request); + return afs_find_cm_server_by_peer(call); } /* -- cgit v1.2.3-70-g09d2 From 194d28cf197ca982556be58dcf687b43fd85e9cc Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 27 Apr 2020 13:42:53 +0100 Subject: afs: Retain more of the VLDB record for alias detection Save more bits from the volume location database record obtained for a server so that we can use this information in cell alias detection. Signed-off-by: David Howells --- fs/afs/internal.h | 3 ++- fs/afs/server_list.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'fs/afs') diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 9f024c1bd650..dce03e068cab 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -546,7 +546,7 @@ struct afs_cb_interest { }; /* - * Replaceable server list. + * Replaceable volume server list. */ struct afs_server_entry { struct afs_server *server; @@ -554,6 +554,7 @@ struct afs_server_entry { }; struct afs_server_list { + afs_volid_t vids[AFS_MAXTYPES]; /* Volume IDs */ refcount_t usage; unsigned char nr_servers; unsigned char preferred; /* Preferred server */ diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index b77e50f62459..a35f6951a74a 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -46,6 +46,9 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell, refcount_set(&slist->usage, 1); rwlock_init(&slist->lock); + for (i = 0; i < AFS_MAXTYPES; i++) + slist->vids[i] = vldb->vid[i]; + /* Make sure a records exists for each server in the list. */ for (i = 0; i < vldb->nr_servers; i++) { if (!(vldb->fs_mask[i] & type_mask)) -- cgit v1.2.3-70-g09d2 From c3e9f888263bb4df11cbd623ceced02081cb2f9f Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 29 Apr 2020 17:26:41 +0100 Subject: afs: Implement client support for the YFSVL.GetCellName RPC op Implement client support for the YFSVL.GetCellName RPC operation by which YFS permits the canonical cell name to be queried from a VL server. Signed-off-by: David Howells --- fs/afs/afs.h | 2 +- fs/afs/afs_vl.h | 1 + fs/afs/internal.h | 2 + fs/afs/protocol_yfs.h | 2 +- fs/afs/vlclient.c | 111 +++++++++++++++++++++++++++++++++++++++++++++ include/trace/events/afs.h | 4 ++ 6 files changed, 120 insertions(+), 2 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/afs.h b/fs/afs/afs.h index f8e34406243e..432cb4b23961 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h @@ -10,7 +10,7 @@ #include -#define AFS_MAXCELLNAME 64 /* Maximum length of a cell name */ +#define AFS_MAXCELLNAME 256 /* Maximum length of a cell name */ #define AFS_MAXVOLNAME 64 /* Maximum length of a volume name */ #define AFS_MAXNSERVERS 8 /* Maximum servers in a basic volume record */ #define AFS_NMAXNSERVERS 13 /* Maximum servers in a N/U-class volume record */ diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h index e9b8029920ec..9c65ffb8a523 100644 --- a/fs/afs/afs_vl.h +++ b/fs/afs/afs_vl.h @@ -22,6 +22,7 @@ enum AFSVL_Operations { VLGETENTRYBYNAMEU = 527, /* AFS Get VLDB entry by name (UUID-variant) */ VLGETADDRSU = 533, /* AFS Get addrs for fileserver */ YVLGETENDPOINTS = 64002, /* YFS Get endpoints for file/volume server */ + YVLGETCELLNAME = 64014, /* YFS Get actual cell name */ VLGETCAPABILITIES = 65537, /* AFS Get server capabilities */ }; diff --git a/fs/afs/internal.h b/fs/afs/internal.h index dce03e068cab..3606cfa50832 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -116,6 +116,7 @@ struct afs_call { long ret0; /* Value to reply with instead of 0 */ struct afs_addr_list *ret_alist; struct afs_vldb_entry *ret_vldb; + char *ret_str; }; struct afs_operation *op; unsigned int server_index; @@ -1373,6 +1374,7 @@ extern struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *, const uu extern struct afs_call *afs_vl_get_capabilities(struct afs_net *, struct afs_addr_cursor *, struct key *, struct afs_vlserver *, unsigned int); extern struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *, const uuid_t *); +extern char *afs_yfsvl_get_cell_name(struct afs_vl_cursor *); /* * vl_probe.c diff --git a/fs/afs/protocol_yfs.h b/fs/afs/protocol_yfs.h index 32be9c698348..b5bd03b1d3c7 100644 --- a/fs/afs/protocol_yfs.h +++ b/fs/afs/protocol_yfs.h @@ -8,7 +8,7 @@ #define YFS_FS_SERVICE 2500 #define YFS_CM_SERVICE 2501 -#define YFSCBMAX 1024 +#define YFSCBMAX 1024 enum YFS_CM_Operations { YFSCBProbe = 206, /* probe client */ diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c index d0c85623ce8f..fd82850cd424 100644 --- a/fs/afs/vlclient.c +++ b/fs/afs/vlclient.c @@ -645,3 +645,114 @@ struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc, afs_make_call(&vc->ac, call, GFP_KERNEL); return (struct afs_addr_list *)afs_wait_for_call_to_complete(call, &vc->ac); } + +/* + * Deliver reply data to a YFSVL.GetCellName operation. + */ +static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call) +{ + char *cell_name; + u32 namesz, paddedsz; + int ret; + + _enter("{%u,%zu/%u}", + call->unmarshall, iov_iter_count(call->iter), call->count); + + switch (call->unmarshall) { + case 0: + afs_extract_to_tmp(call); + call->unmarshall++; + + /* Fall through - and extract the cell name length */ + case 1: + ret = afs_extract_data(call, true); + if (ret < 0) + return ret; + + namesz = ntohl(call->tmp); + if (namesz > AFS_MAXCELLNAME) + return afs_protocol_error(call, afs_eproto_cellname_len); + paddedsz = (namesz + 3) & ~3; + call->count = namesz; + call->count2 = paddedsz - namesz; + + cell_name = kmalloc(namesz + 1, GFP_KERNEL); + if (!cell_name) + return -ENOMEM; + cell_name[namesz] = 0; + call->ret_str = cell_name; + + afs_extract_begin(call, cell_name, namesz); + call->unmarshall++; + + /* Fall through - and extract cell name */ + case 2: + ret = afs_extract_data(call, true); + if (ret < 0) + return ret; + + afs_extract_discard(call, call->count2); + call->unmarshall++; + + /* Fall through - and extract padding */ + case 3: + ret = afs_extract_data(call, false); + if (ret < 0) + return ret; + + call->unmarshall++; + break; + } + + _leave(" = 0 [done]"); + return 0; +} + +static void afs_destroy_yfsvl_get_cell_name(struct afs_call *call) +{ + kfree(call->ret_str); + afs_flat_call_destructor(call); +} + +/* + * VL.GetCapabilities operation type + */ +static const struct afs_call_type afs_YFSVLGetCellName = { + .name = "YFSVL.GetCellName", + .op = afs_YFSVL_GetCellName, + .deliver = afs_deliver_yfsvl_get_cell_name, + .destructor = afs_destroy_yfsvl_get_cell_name, +}; + +/* + * Probe a volume server for the capabilities that it supports. This can + * return up to 196 words. + * + * We use this to probe for service upgrade to determine what the server at the + * other end supports. + */ +char *afs_yfsvl_get_cell_name(struct afs_vl_cursor *vc) +{ + struct afs_call *call; + struct afs_net *net = vc->cell->net; + __be32 *bp; + + _enter(""); + + call = afs_alloc_flat_call(net, &afs_YFSVLGetCellName, 1 * 4, 0); + if (!call) + return ERR_PTR(-ENOMEM); + + call->key = vc->key; + call->ret_str = NULL; + call->max_lifespan = AFS_VL_MAX_LIFESPAN; + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(YVLGETCELLNAME); + + /* Can't take a ref on server */ + trace_afs_make_vl_call(call); + afs_make_call(&vc->ac, call, GFP_KERNEL); + return (char *)afs_wait_for_call_to_complete(call, &vc->ac); +} diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index f4d66919fb22..f320b3ad54da 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -111,6 +111,7 @@ enum afs_vl_operation { afs_VL_GetEntryByNameU = 527, /* AFS Get Vol Entry By Name operation ID */ afs_VL_GetAddrsU = 533, /* AFS Get FS server addresses */ afs_YFSVL_GetEndpoints = 64002, /* YFS Get FS & Vol server addresses */ + afs_YFSVL_GetCellName = 64014, /* YFS Get actual cell name */ afs_VL_GetCapabilities = 65537, /* AFS Get VL server capabilities */ }; @@ -143,6 +144,7 @@ enum afs_eproto_cause { afs_eproto_bad_status, afs_eproto_cb_count, afs_eproto_cb_fid_count, + afs_eproto_cellname_len, afs_eproto_file_type, afs_eproto_ibulkst_cb_count, afs_eproto_ibulkst_count, @@ -316,6 +318,7 @@ enum afs_cb_break_reason { EM(afs_VL_GetEntryByNameU, "VL.GetEntryByNameU") \ EM(afs_VL_GetAddrsU, "VL.GetAddrsU") \ EM(afs_YFSVL_GetEndpoints, "YFSVL.GetEndpoints") \ + EM(afs_YFSVL_GetCellName, "YFSVL.GetCellName") \ E_(afs_VL_GetCapabilities, "VL.GetCapabilities") #define afs_edit_dir_ops \ @@ -345,6 +348,7 @@ enum afs_cb_break_reason { EM(afs_eproto_bad_status, "BadStatus") \ EM(afs_eproto_cb_count, "CbCount") \ EM(afs_eproto_cb_fid_count, "CbFidCount") \ + EM(afs_eproto_cellname_len, "CellNameLen") \ EM(afs_eproto_file_type, "FileTYpe") \ EM(afs_eproto_ibulkst_cb_count, "IBS.CbCount") \ EM(afs_eproto_ibulkst_count, "IBS.FidCount") \ -- cgit v1.2.3-70-g09d2 From 8a070a964877c71139cba46202f6f263c2b9419d Mon Sep 17 00:00:00 2001 From: David Howells Date: Sat, 25 Apr 2020 10:26:02 +0100 Subject: afs: Detect cell aliases 1 - Cells with root volumes Put in the first phase of cell alias detection. This part handles alias detection for cells that have root.cell volumes (which is expected to be likely). When a cell becomes newly active, it is probed for its root.cell volume, and if it has one, this volume is compared against other root.cell volumes to find out if the list of fileserver UUIDs have any in common - and if that's the case, do the address lists of those fileservers have any addresses in common. If they do, the new cell is adjudged to be an alias of the old cell and the old cell is used instead. Comparing is aided by the server list in struct afs_server_list being sorted in UUID order and the addresses in the fileserver address lists being sorted in address order. The cell then retains the afs_volume object for the root.cell volume, even if it's not mounted for future alias checking. This necessary because: (1) Whilst fileservers have UUIDs that are meant to be globally unique, in practice they are not because cells get cloned without changing the UUIDs - so afs_server records need to be per cell. (2) Sometimes the DNS is used to make cell aliases - but if we don't know they're the same, we may end up with multiple superblocks and multiple afs_server records for the same thing, impairing our ability to deliver callback notifications of third party changes (3) The fileserver RPC API doesn't contain the cell name, so it can't tell us which cell it's notifying and can't see that a change made to to one cell should notify the same client that's also accessed as the other cell. Reported-by: Jeffrey Altman Signed-off-by: David Howells --- fs/afs/Makefile | 1 + fs/afs/cell.c | 3 + fs/afs/internal.h | 17 +++- fs/afs/main.c | 1 + fs/afs/proc.c | 5 +- fs/afs/rotate.c | 8 +- fs/afs/super.c | 21 ++++- fs/afs/vl_alias.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/afs/volume.c | 9 ++- 9 files changed, 287 insertions(+), 13 deletions(-) create mode 100644 fs/afs/vl_alias.c (limited to 'fs/afs') diff --git a/fs/afs/Makefile b/fs/afs/Makefile index 924f02e9d7e7..75c4e4043d1d 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile @@ -31,6 +31,7 @@ kafs-y := \ server_list.o \ super.o \ vlclient.o \ + vl_alias.o \ vl_list.o \ vl_probe.o \ vl_rotate.o \ diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 78ba5f932287..212098514ebf 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -164,6 +164,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, INIT_LIST_HEAD(&cell->proc_volumes); rwlock_init(&cell->proc_lock); rwlock_init(&cell->vl_servers_lock); + cell->flags = (1 << AFS_CELL_FL_CHECK_ALIAS); /* Provide a VL server list, filling it in if we were given a list of * addresses to use. @@ -481,7 +482,9 @@ static void afs_cell_destroy(struct rcu_head *rcu) ASSERTCMP(atomic_read(&cell->usage), ==, 0); + afs_put_volume(cell->net, cell->root_volume); afs_put_vlserverlist(cell->net, rcu_access_pointer(cell->vl_servers)); + afs_put_cell(cell->net, cell->alias_of); key_put(cell->anonymous_key); kfree(cell); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 3606cfa50832..a3ef97d560ca 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -269,6 +269,7 @@ struct afs_net { struct timer_list cells_timer; atomic_t cells_outstanding; seqlock_t cells_lock; + struct mutex cells_alias_lock; struct mutex proc_cells_lock; struct hlist_head proc_cells; @@ -342,8 +343,10 @@ enum afs_cell_state { * for authentication and encryption. The cell name is not typically used in * the protocol. * - * There is no easy way to determine if two cells are aliases or one is a - * subset of another. + * Two cells are determined to be aliases if they have an explicit alias (YFS + * only), share any VL servers in common or have at least one volume in common. + * "In common" means that the address list of the VL servers or the fileservers + * share at least one endpoint. */ struct afs_cell { union { @@ -351,6 +354,8 @@ struct afs_cell { struct rb_node net_node; /* Node in net->cells */ }; struct afs_net *net; + struct afs_cell *alias_of; /* The cell this is an alias of */ + struct afs_volume *root_volume; /* The root.cell volume if there is one */ struct key *anonymous_key; /* anonymous user key for this cell */ struct work_struct manager; /* Manager for init/deinit/dns */ struct hlist_node proc_link; /* /proc cell list link */ @@ -363,6 +368,7 @@ struct afs_cell { unsigned long flags; #define AFS_CELL_FL_NO_GC 0 /* The cell was added manually, don't auto-gc */ #define AFS_CELL_FL_DO_LOOKUP 1 /* DNS lookup requested */ +#define AFS_CELL_FL_CHECK_ALIAS 2 /* Need to check for aliases */ enum afs_cell_state state; short error; enum dns_record_source dns_source:8; /* Latest source of data from lookup */ @@ -584,7 +590,7 @@ struct afs_volume { #ifdef CONFIG_AFS_FSCACHE struct fscache_cookie *cache; /* caching cookie */ #endif - struct afs_server_list *servers; /* List of servers on which volume resides */ + struct afs_server_list __rcu *servers; /* List of servers on which volume resides */ rwlock_t servers_lock; /* Lock for ->servers */ unsigned int servers_seq; /* Incremented each time ->servers changes */ @@ -1376,6 +1382,11 @@ extern struct afs_call *afs_vl_get_capabilities(struct afs_net *, struct afs_add extern struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *, const uuid_t *); extern char *afs_yfsvl_get_cell_name(struct afs_vl_cursor *); +/* + * vl_alias.c + */ +extern int afs_cell_detect_alias(struct afs_cell *, struct key *); + /* * vl_probe.c */ diff --git a/fs/afs/main.c b/fs/afs/main.c index 56b52f8dbf15..9c79c91e8005 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c @@ -82,6 +82,7 @@ static int __net_init afs_net_init(struct net *net_ns) INIT_WORK(&net->cells_manager, afs_manage_cells); timer_setup(&net->cells_timer, afs_cells_timer, 0); + mutex_init(&net->cells_alias_lock); mutex_init(&net->proc_cells_lock); INIT_HLIST_HEAD(&net->proc_cells); diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 1d21465a4108..256c3eff8c82 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -38,7 +38,7 @@ static int afs_proc_cells_show(struct seq_file *m, void *v) if (v == SEQ_START_TOKEN) { /* display header on line 1 */ - seq_puts(m, "USE TTL SV NAME\n"); + seq_puts(m, "USE TTL SV ST NAME\n"); return 0; } @@ -46,10 +46,11 @@ static int afs_proc_cells_show(struct seq_file *m, void *v) vllist = rcu_dereference(cell->vl_servers); /* display one cell per line on subsequent lines */ - seq_printf(m, "%3u %6lld %2u %s\n", + seq_printf(m, "%3u %6lld %2u %2u %s\n", atomic_read(&cell->usage), cell->dns_expiry - ktime_get_real_seconds(), vllist->nr_servers, + cell->state, cell->name); return 0; } diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index 8c8dc2397c5d..d67b8c7eb3b9 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -25,7 +25,9 @@ static bool afs_start_fs_iteration(struct afs_operation *op, int i; read_lock(&op->volume->servers_lock); - op->server_list = afs_get_serverlist(op->volume->servers); + op->server_list = afs_get_serverlist( + rcu_dereference_protected(op->volume->servers, + lockdep_is_held(&op->volume->servers_lock))); read_unlock(&op->volume->servers_lock); op->untried = (1UL << op->server_list->nr_servers) - 1; @@ -173,7 +175,7 @@ bool afs_select_fileserver(struct afs_operation *op) /* If the server list didn't change, then assume that * it's the fileserver having trouble. */ - if (op->volume->servers == op->server_list) { + if (rcu_access_pointer(op->volume->servers) == op->server_list) { op->error = -EREMOTEIO; goto next_server; } @@ -263,7 +265,7 @@ bool afs_select_fileserver(struct afs_operation *op) * * TODO: Retry a few times with sleeps. */ - if (op->volume->servers == op->server_list) { + if (rcu_access_pointer(op->volume->servers) == op->server_list) { op->error = -ENOMEDIUM; goto failed; } diff --git a/fs/afs/super.c b/fs/afs/super.c index c4bb314a22ae..aae6866ed209 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -352,7 +352,9 @@ static int afs_validate_fc(struct fs_context *fc) { struct afs_fs_context *ctx = fc->fs_private; struct afs_volume *volume; + struct afs_cell *cell; struct key *key; + int ret; if (!ctx->dyn_root) { if (ctx->no_cell) { @@ -365,6 +367,7 @@ static int afs_validate_fc(struct fs_context *fc) return -EDESTADDRREQ; } + reget_key: /* We try to do the mount securely. */ key = afs_request_key(ctx->cell); if (IS_ERR(key)) @@ -377,6 +380,21 @@ static int afs_validate_fc(struct fs_context *fc) ctx->volume = NULL; } + if (test_bit(AFS_CELL_FL_CHECK_ALIAS, &ctx->cell->flags)) { + ret = afs_cell_detect_alias(ctx->cell, key); + if (ret < 0) + return ret; + if (ret == 1) { + _debug("switch to alias"); + key_put(ctx->key); + ctx->key = NULL; + cell = afs_get_cell(ctx->cell->alias_of); + afs_put_cell(ctx->net, ctx->cell); + ctx->cell = cell; + goto reget_key; + } + } + volume = afs_create_volume(ctx); if (IS_ERR(volume)) return PTR_ERR(volume); @@ -518,7 +536,8 @@ static void afs_kill_super(struct super_block *sb) * deactivating the superblock. */ if (as->volume) - afs_clear_callback_interests(net, as->volume->servers); + afs_clear_callback_interests( + net, rcu_access_pointer(as->volume->servers)); kill_anon_super(sb); if (as->volume) afs_deactivate_volume(as->volume); diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c new file mode 100644 index 000000000000..d1d91a25fbe0 --- /dev/null +++ b/fs/afs/vl_alias.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* AFS cell alias detection + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include +#include "internal.h" + +/* + * Sample a volume. + */ +static struct afs_volume *afs_sample_volume(struct afs_cell *cell, struct key *key, + const char *name, unsigned int namelen) +{ + struct afs_volume *volume; + struct afs_fs_context fc = { + .type = 0, /* Explicitly leave it to the VLDB */ + .volnamesz = namelen, + .volname = name, + .net = cell->net, + .cell = cell, + .key = key, /* This might need to be something */ + }; + + volume = afs_create_volume(&fc); + _leave(" = %px", volume); + return volume; +} + +/* + * Compare two addresses. + */ +static int afs_compare_addrs(const struct sockaddr_rxrpc *srx_a, + const struct sockaddr_rxrpc *srx_b) +{ + short port_a, port_b; + int addr_a, addr_b, diff; + + diff = (short)srx_a->transport_type - (short)srx_b->transport_type; + if (diff) + goto out; + + switch (srx_a->transport_type) { + case AF_INET: { + const struct sockaddr_in *a = &srx_a->transport.sin; + const struct sockaddr_in *b = &srx_b->transport.sin; + addr_a = ntohl(a->sin_addr.s_addr); + addr_b = ntohl(b->sin_addr.s_addr); + diff = addr_a - addr_b; + if (diff == 0) { + port_a = ntohs(a->sin_port); + port_b = ntohs(b->sin_port); + diff = port_a - port_b; + } + break; + } + + case AF_INET6: { + const struct sockaddr_in6 *a = &srx_a->transport.sin6; + const struct sockaddr_in6 *b = &srx_b->transport.sin6; + diff = memcmp(&a->sin6_addr, &b->sin6_addr, 16); + if (diff == 0) { + port_a = ntohs(a->sin6_port); + port_b = ntohs(b->sin6_port); + diff = port_a - port_b; + } + break; + } + + default: + BUG(); + } + +out: + return diff; +} + +/* + * Compare the address lists of a pair of fileservers. + */ +static int afs_compare_fs_alists(const struct afs_server *server_a, + const struct afs_server *server_b) +{ + const struct afs_addr_list *la, *lb; + int a = 0, b = 0, addr_matches = 0; + + la = rcu_dereference(server_a->addresses); + lb = rcu_dereference(server_b->addresses); + + while (a < la->nr_addrs && b < lb->nr_addrs) { + const struct sockaddr_rxrpc *srx_a = &la->addrs[a]; + const struct sockaddr_rxrpc *srx_b = &lb->addrs[b]; + int diff = afs_compare_addrs(srx_a, srx_b); + + if (diff < 0) { + a++; + } else if (diff > 0) { + b++; + } else { + addr_matches++; + a++; + b++; + } + } + + return addr_matches; +} + +/* + * Compare the fileserver lists of two volumes. The server lists are sorted in + * order of ascending UUID. + */ +static int afs_compare_volume_slists(const struct afs_volume *vol_a, + const struct afs_volume *vol_b) +{ + const struct afs_server_list *la, *lb; + int i, a = 0, b = 0, uuid_matches = 0, addr_matches = 0; + + la = rcu_dereference(vol_a->servers); + lb = rcu_dereference(vol_b->servers); + + for (i = 0; i < AFS_MAXTYPES; i++) + if (la->vids[i] != lb->vids[i]) + return 0; + + while (a < la->nr_servers && b < lb->nr_servers) { + const struct afs_server *server_a = la->servers[a].server; + const struct afs_server *server_b = lb->servers[b].server; + int diff = memcmp(&server_a->uuid, &server_b->uuid, sizeof(uuid_t)); + + if (diff < 0) { + a++; + } else if (diff > 0) { + b++; + } else { + uuid_matches++; + addr_matches += afs_compare_fs_alists(server_a, server_b); + a++; + b++; + } + } + + _leave(" = %d [um %d]", addr_matches, uuid_matches); + return addr_matches; +} + +/* + * Compare root.cell volumes. + */ +static int afs_compare_cell_roots(struct afs_cell *cell) +{ + struct afs_cell *p; + + _enter(""); + + rcu_read_lock(); + + hlist_for_each_entry_rcu(p, &cell->net->proc_cells, proc_link) { + if (p == cell || p->alias_of) + continue; + if (!p->root_volume) + continue; /* Ignore cells that don't have a root.cell volume. */ + + if (afs_compare_volume_slists(cell->root_volume, p->root_volume) != 0) + goto is_alias; + } + + rcu_read_unlock(); + _leave(" = 0"); + return 0; + +is_alias: + rcu_read_unlock(); + cell->alias_of = afs_get_cell(p); + return 1; +} + +static int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key) +{ + struct afs_volume *root_volume; + + _enter("%s", cell->name); + + /* Try and get the root.cell volume for comparison with other cells */ + root_volume = afs_sample_volume(cell, key, "root.cell", 9); + if (!IS_ERR(root_volume)) { + cell->root_volume = root_volume; + return afs_compare_cell_roots(cell); + } + + if (PTR_ERR(root_volume) != -ENOMEDIUM) + return PTR_ERR(root_volume); + + /* Okay, this cell doesn't have an root.cell volume. We need to + * locate some other random volume and use that to check. + */ + return -ENOMEDIUM; +} + +/* + * Check to see if a new cell is an alias of a cell we already have. At this + * point we have the cell's volume server list. + * + * Returns 0 if we didn't detect an alias, 1 if we found an alias and an error + * if we had problems gathering the data required. In the case the we did + * detect an alias, cell->alias_of is set to point to the assumed master. + */ +int afs_cell_detect_alias(struct afs_cell *cell, struct key *key) +{ + struct afs_net *net = cell->net; + int ret; + + if (mutex_lock_interruptible(&net->cells_alias_lock) < 0) + return -ERESTARTSYS; + + if (test_bit(AFS_CELL_FL_CHECK_ALIAS, &cell->flags)) { + ret = afs_do_cell_detect_alias(cell, key); + if (ret >= 0) + clear_bit_unlock(AFS_CELL_FL_CHECK_ALIAS, &cell->flags); + } else { + ret = cell->alias_of ? 1 : 0; + } + + mutex_unlock(&net->cells_alias_lock); + + if (ret == 1) + pr_notice("kAFS: Cell %s is an alias of %s\n", + cell->name, cell->alias_of->name); + return ret; +} diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 57d0509f7353..d4d9a8fbba3d 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -51,7 +51,7 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params, } refcount_set(&slist->usage, 1); - volume->servers = slist; + rcu_assign_pointer(volume->servers, slist); return volume; error_1: @@ -156,7 +156,7 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume) ASSERTCMP(volume->cache, ==, NULL); #endif - afs_put_serverlist(net, volume->servers); + afs_put_serverlist(net, rcu_access_pointer(volume->servers)); afs_put_cell(net, volume->cell); kfree(volume); @@ -256,10 +256,11 @@ static int afs_update_volume_status(struct afs_volume *volume, struct key *key) write_lock(&volume->servers_lock); discard = new; - old = volume->servers; + old = rcu_dereference_protected(volume->servers, + lockdep_is_held(&volume->servers_lock)); if (afs_annotate_server_list(new, old)) { new->seq = volume->servers_seq + 1; - volume->servers = new; + rcu_assign_pointer(volume->servers, new); smp_wmb(); volume->servers_seq++; discard = old; -- cgit v1.2.3-70-g09d2 From 6ef350b1842081bef7e4879993f47f052b7007e7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 26 Apr 2020 10:12:27 +0100 Subject: afs: Detect cell aliases 2 - Cells with no root volumes Implement the second phase of cell alias detection. This part handles alias detection for cells that don't have root.cell volumes and so we have to find some other volume or fileserver to query. We take the first volume from each such cell and attempt to look it up in the new cell. If found, we compare the records, if they are the same, we judge the cell names to be aliases. Signed-off-by: David Howells --- fs/afs/vl_alias.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) (limited to 'fs/afs') diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c index d1d91a25fbe0..76bfa4dde4a4 100644 --- a/fs/afs/vl_alias.c +++ b/fs/afs/vl_alias.c @@ -180,6 +180,94 @@ is_alias: return 1; } +/* + * Query the new cell for a volume from a cell we're already using. + */ +static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key, + struct afs_cell *p) +{ + struct afs_volume *volume, *pvol = NULL; + int ret; + + /* Arbitrarily pick the first volume in the list. */ + read_lock(&p->proc_lock); + if (!list_empty(&p->proc_volumes)) + pvol = afs_get_volume(list_first_entry(&p->proc_volumes, + struct afs_volume, proc_link)); + read_unlock(&p->proc_lock); + if (!pvol) + return 0; + + _enter("%s:%s", cell->name, pvol->name); + + /* And see if it's in the new cell. */ + volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len); + if (IS_ERR(volume)) { + afs_put_volume(cell->net, pvol); + if (PTR_ERR(volume) != -ENOMEDIUM) + return PTR_ERR(volume); + /* That volume is not in the new cell, so not an alias */ + return 0; + } + + /* The new cell has a like-named volume also - compare volume ID, + * server and address lists. + */ + ret = 0; + if (pvol->vid == volume->vid) { + rcu_read_lock(); + if (afs_compare_volume_slists(volume, pvol)) + ret = 1; + rcu_read_unlock(); + } + + afs_put_volume(cell->net, volume); + afs_put_volume(cell->net, pvol); + return ret; +} + +/* + * Query the new cell for volumes we know exist in cells we're already using. + */ +static int afs_query_for_alias(struct afs_cell *cell, struct key *key) +{ + struct afs_cell *p; + + _enter("%s", cell->name); + + if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0) + return -ERESTARTSYS; + + hlist_for_each_entry(p, &cell->net->proc_cells, proc_link) { + if (p == cell || p->alias_of) + continue; + if (list_empty(&p->proc_volumes)) + continue; + if (p->root_volume) + continue; /* Ignore cells that have a root.cell volume. */ + afs_get_cell(p); + mutex_unlock(&cell->net->proc_cells_lock); + + if (afs_query_for_alias_one(cell, key, p) != 0) + goto is_alias; + + if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0) { + afs_put_cell(cell->net, p); + return -ERESTARTSYS; + } + + afs_put_cell(cell->net, p); + } + + mutex_unlock(&cell->net->proc_cells_lock); + _leave(" = 0"); + return 0; + +is_alias: + cell->alias_of = p; /* Transfer our ref */ + return 1; +} + static int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key) { struct afs_volume *root_volume; @@ -199,7 +287,7 @@ static int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key) /* Okay, this cell doesn't have an root.cell volume. We need to * locate some other random volume and use that to check. */ - return -ENOMEDIUM; + return afs_query_for_alias(cell, key); } /* -- cgit v1.2.3-70-g09d2 From 6dfdf5369c9f0a47920b2f743434c90798f26cd5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 27 Apr 2020 15:01:09 +0100 Subject: afs: Detect cell aliases 3 - YFS Cells with a canonical cell name op YFS Volume Location servers have an operation by which the cell name may be queried. Use this to find out what a YFS server thinks the canonical cell name should be. Signed-off-by: David Howells --- fs/afs/vl_alias.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/afs/vl_rotate.c | 4 ++++ 2 files changed, 62 insertions(+) (limited to 'fs/afs') diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c index 76bfa4dde4a4..ac7a59e951ed 100644 --- a/fs/afs/vl_alias.c +++ b/fs/afs/vl_alias.c @@ -268,12 +268,70 @@ is_alias: return 1; } +/* + * Look up a VLDB record for a volume. + */ +static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key) +{ + struct afs_vl_cursor vc; + char *cell_name = ERR_PTR(-EDESTADDRREQ); + bool skipped = false, not_skipped = false; + int ret; + + if (!afs_begin_vlserver_operation(&vc, cell, key)) + return ERR_PTR(-ERESTARTSYS); + + while (afs_select_vlserver(&vc)) { + if (!test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags)) { + vc.ac.error = -EOPNOTSUPP; + skipped = true; + continue; + } + not_skipped = true; + cell_name = afs_yfsvl_get_cell_name(&vc); + } + + ret = afs_end_vlserver_operation(&vc); + if (skipped && !not_skipped) + ret = -EOPNOTSUPP; + return ret < 0 ? ERR_PTR(ret) : cell_name; +} + +static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key) +{ + struct afs_cell *master; + char *cell_name; + + cell_name = afs_vl_get_cell_name(cell, key); + if (IS_ERR(cell_name)) + return PTR_ERR(cell_name); + + if (strcmp(cell_name, cell->name) == 0) { + kfree(cell_name); + return 0; + } + + master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name), + NULL, false); + kfree(cell_name); + if (IS_ERR(master)) + return PTR_ERR(master); + + cell->alias_of = master; /* Transfer our ref */ + return 1; +} + static int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key) { struct afs_volume *root_volume; + int ret; _enter("%s", cell->name); + ret = yfs_check_canonical_cell_name(cell, key); + if (ret != -EOPNOTSUPP) + return ret; + /* Try and get the root.cell volume for comparison with other cells */ root_volume = afs_sample_volume(cell, key, "root.cell", 9); if (!IS_ERR(root_volume)) { diff --git a/fs/afs/vl_rotate.c b/fs/afs/vl_rotate.c index 72eacc14e6e1..f405ca8b240a 100644 --- a/fs/afs/vl_rotate.c +++ b/fs/afs/vl_rotate.c @@ -151,6 +151,10 @@ bool afs_select_vlserver(struct afs_vl_cursor *vc) vc->error = error; vc->flags |= AFS_VL_CURSOR_RETRY; goto next_server; + + case -EOPNOTSUPP: + _debug("notsupp"); + goto next_server; } restart_from_beginning: -- cgit v1.2.3-70-g09d2 From cca37d45d547434144409ae648a19b7eb6db5eb4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 29 Apr 2020 17:02:04 +0100 Subject: afs: Add a tracepoint to track the lifetime of the afs_volume struct Add a tracepoint to track the lifetime of the afs_volume struct. Signed-off-by: David Howells --- fs/afs/cell.c | 2 +- fs/afs/fs_operation.c | 4 ++-- fs/afs/internal.h | 10 ++------- fs/afs/super.c | 10 +++++---- fs/afs/vl_alias.c | 9 ++++---- fs/afs/volume.c | 27 ++++++++++++++++++---- include/trace/events/afs.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 95 insertions(+), 23 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 212098514ebf..8bfc8a05fd46 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -482,7 +482,7 @@ static void afs_cell_destroy(struct rcu_head *rcu) ASSERTCMP(atomic_read(&cell->usage), ==, 0); - afs_put_volume(cell->net, cell->root_volume); + afs_put_volume(cell->net, cell->root_volume, afs_volume_trace_put_cell_root); afs_put_vlserverlist(cell->net, rcu_access_pointer(cell->vl_servers)); afs_put_cell(cell->net, cell->alias_of); key_put(cell->anonymous_key); diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c index f7a768d12141..f57efd9d2db0 100644 --- a/fs/afs/fs_operation.c +++ b/fs/afs/fs_operation.c @@ -36,7 +36,7 @@ struct afs_operation *afs_alloc_operation(struct key *key, struct afs_volume *vo } op->key = key; - op->volume = afs_get_volume(volume); + op->volume = afs_get_volume(volume, afs_volume_trace_get_new_op); op->net = volume->cell->net; op->cb_v_break = volume->cb_v_break; op->debug_id = atomic_inc_return(&afs_operation_debug_counter); @@ -233,7 +233,7 @@ int afs_put_operation(struct afs_operation *op) afs_end_cursor(&op->ac); afs_put_cb_interest(op->net, op->cbi); afs_put_serverlist(op->net, op->server_list); - afs_put_volume(op->net, op->volume); + afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op); kfree(op); return ret; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index a3ef97d560ca..e084936066b0 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1429,17 +1429,11 @@ extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *, /* * volume.c */ -static inline struct afs_volume *afs_get_volume(struct afs_volume *volume) -{ - if (volume) - atomic_inc(&volume->usage); - return volume; -} - extern struct afs_volume *afs_create_volume(struct afs_fs_context *); extern void afs_activate_volume(struct afs_volume *); extern void afs_deactivate_volume(struct afs_volume *); -extern void afs_put_volume(struct afs_net *, struct afs_volume *); +extern struct afs_volume *afs_get_volume(struct afs_volume *, enum afs_volume_trace); +extern void afs_put_volume(struct afs_net *, struct afs_volume *, enum afs_volume_trace); extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *); /* diff --git a/fs/afs/super.c b/fs/afs/super.c index aae6866ed209..f92ccdafc729 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -376,7 +376,8 @@ static int afs_validate_fc(struct fs_context *fc) ctx->key = key; if (ctx->volume) { - afs_put_volume(ctx->net, ctx->volume); + afs_put_volume(ctx->net, ctx->volume, + afs_volume_trace_put_validate_fc); ctx->volume = NULL; } @@ -507,7 +508,8 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc) as->dyn_root = true; } else { as->cell = afs_get_cell(ctx->cell); - as->volume = afs_get_volume(ctx->volume); + as->volume = afs_get_volume(ctx->volume, + afs_volume_trace_get_alloc_sbi); } } return as; @@ -517,7 +519,7 @@ static void afs_destroy_sbi(struct afs_super_info *as) { if (as) { struct afs_net *net = afs_net(as->net_ns); - afs_put_volume(net, as->volume); + afs_put_volume(net, as->volume, afs_volume_trace_put_destroy_sbi); afs_put_cell(net, as->cell); put_net(as->net_ns); kfree(as); @@ -605,7 +607,7 @@ static void afs_free_fc(struct fs_context *fc) struct afs_fs_context *ctx = fc->fs_private; afs_destroy_sbi(fc->s_fs_info); - afs_put_volume(ctx->net, ctx->volume); + afs_put_volume(ctx->net, ctx->volume, afs_volume_trace_put_free_fc); afs_put_cell(ctx->net, ctx->cell); key_put(ctx->key); kfree(ctx); diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c index ac7a59e951ed..c61dd9410202 100644 --- a/fs/afs/vl_alias.c +++ b/fs/afs/vl_alias.c @@ -193,7 +193,8 @@ static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key, read_lock(&p->proc_lock); if (!list_empty(&p->proc_volumes)) pvol = afs_get_volume(list_first_entry(&p->proc_volumes, - struct afs_volume, proc_link)); + struct afs_volume, proc_link), + afs_volume_trace_get_query_alias); read_unlock(&p->proc_lock); if (!pvol) return 0; @@ -203,7 +204,7 @@ static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key, /* And see if it's in the new cell. */ volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len); if (IS_ERR(volume)) { - afs_put_volume(cell->net, pvol); + afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias); if (PTR_ERR(volume) != -ENOMEDIUM) return PTR_ERR(volume); /* That volume is not in the new cell, so not an alias */ @@ -221,8 +222,8 @@ static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key, rcu_read_unlock(); } - afs_put_volume(cell->net, volume); - afs_put_volume(cell->net, pvol); + afs_put_volume(cell->net, volume, afs_volume_trace_put_query_alias); + afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias); return ret; } diff --git a/fs/afs/volume.c b/fs/afs/volume.c index d4d9a8fbba3d..0393f4910a92 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -52,6 +52,7 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params, refcount_set(&slist->usage, 1); rcu_assign_pointer(volume->servers, slist); + trace_afs_volume(volume->vid, 1, afs_volume_trace_alloc); return volume; error_1: @@ -158,20 +159,38 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume) afs_put_serverlist(net, rcu_access_pointer(volume->servers)); afs_put_cell(net, volume->cell); + trace_afs_volume(volume->vid, atomic_read(&volume->usage), + afs_volume_trace_free); kfree(volume); _leave(" [destroyed]"); } /* - * Drop a reference on a volume record. + * Get a reference on a volume record. */ -void afs_put_volume(struct afs_net *net, struct afs_volume *volume) +struct afs_volume *afs_get_volume(struct afs_volume *volume, + enum afs_volume_trace reason) { if (volume) { - _enter("%s", volume->name); + int u = atomic_inc_return(&volume->usage); + trace_afs_volume(volume->vid, u, reason); + } + return volume; +} + - if (atomic_dec_and_test(&volume->usage)) +/* + * Drop a reference on a volume record. + */ +void afs_put_volume(struct afs_net *net, struct afs_volume *volume, + enum afs_volume_trace reason) +{ + if (volume) { + afs_volid_t vid = volume->vid; + int u = atomic_dec_return(&volume->usage); + trace_afs_volume(vid, u, reason); + if (u == 0) afs_destroy_volume(net, volume); } } diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index f320b3ad54da..5f0c1cf1ea13 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -50,6 +50,23 @@ enum afs_server_trace { afs_server_trace_update, }; +enum afs_volume_trace { + afs_volume_trace_alloc, + afs_volume_trace_free, + afs_volume_trace_get_alloc_sbi, + afs_volume_trace_get_cell_insert, + afs_volume_trace_get_new_op, + afs_volume_trace_get_query_alias, + afs_volume_trace_put_cell_dup, + afs_volume_trace_put_cell_root, + afs_volume_trace_put_destroy_sbi, + afs_volume_trace_put_free_fc, + afs_volume_trace_put_put_op, + afs_volume_trace_put_query_alias, + afs_volume_trace_put_validate_fc, + afs_volume_trace_remove, +}; + enum afs_fs_operation { afs_FS_FetchData = 130, /* AFS Fetch file data */ afs_FS_FetchACL = 131, /* AFS Fetch file ACL */ @@ -262,6 +279,22 @@ enum afs_cb_break_reason { EM(afs_server_trace_put_uuid_rsq, "PUT u-req") \ E_(afs_server_trace_update, "UPDATE") +#define afs_volume_traces \ + EM(afs_volume_trace_alloc, "ALLOC ") \ + EM(afs_volume_trace_free, "FREE ") \ + EM(afs_volume_trace_get_alloc_sbi, "GET sbi-alloc ") \ + EM(afs_volume_trace_get_cell_insert, "GET cell-insrt") \ + EM(afs_volume_trace_get_new_op, "GET op-new ") \ + EM(afs_volume_trace_get_query_alias, "GET cell-alias") \ + EM(afs_volume_trace_put_cell_dup, "PUT cell-dup ") \ + EM(afs_volume_trace_put_cell_root, "PUT cell-root ") \ + EM(afs_volume_trace_put_destroy_sbi, "PUT sbi-destry") \ + EM(afs_volume_trace_put_free_fc, "PUT fc-free ") \ + EM(afs_volume_trace_put_put_op, "PUT op-put ") \ + EM(afs_volume_trace_put_query_alias, "PUT cell-alias") \ + EM(afs_volume_trace_put_validate_fc, "PUT fc-validat") \ + E_(afs_volume_trace_remove, "REMOVE ") + #define afs_fs_operations \ EM(afs_FS_FetchData, "FS.FetchData") \ EM(afs_FS_FetchStatus, "FS.FetchStatus") \ @@ -1302,6 +1335,29 @@ TRACE_EVENT(afs_server, __entry->active) ); +TRACE_EVENT(afs_volume, + TP_PROTO(afs_volid_t vid, int ref, enum afs_volume_trace reason), + + TP_ARGS(vid, ref, reason), + + TP_STRUCT__entry( + __field(afs_volid_t, vid ) + __field(int, ref ) + __field(enum afs_volume_trace, reason ) + ), + + TP_fast_assign( + __entry->vid = vid; + __entry->ref = ref; + __entry->reason = reason; + ), + + TP_printk("V=%llx %s u=%d", + __entry->vid, + __print_symbolic(__entry->reason, afs_volume_traces), + __entry->ref) + ); + #endif /* _TRACE_AFS_H */ /* This part must be outside protection */ -- cgit v1.2.3-70-g09d2 From 20325960f8750165964a6891a733e4cc15d19076 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 Apr 2020 01:03:49 +0100 Subject: afs: Reorganise volume and server trees to be rooted on the cell Reorganise afs_volume objects such that they're in a tree keyed on volume ID, rooted at on an afs_cell object rather than being in multiple trees, each of which is rooted on an afs_server object. afs_server structs become per-cell and acquire a pointer to the cell. The process of breaking a callback then starts with finding the server by its network address, following that to the cell and then looking up each volume ID in the volume tree. This is simpler than the afs_vol_interest/afs_cb_interest N:M mapping web and allows those structs and the code for maintaining them to be simplified or removed. It does make a couple of things a bit more tricky, though: (1) Operations now start with a volume, not a server, so there can be more than one answer as to whether or not the server we'll end up using supports the FS.InlineBulkStatus RPC. (2) CB RPC operations that specify the server UUID. There's still a tree of servers by UUID on the afs_net struct, but the UUIDs in it aren't guaranteed unique. Signed-off-by: David Howells --- fs/afs/callback.c | 286 ++++++++------------------------------------------ fs/afs/cell.c | 7 +- fs/afs/dir.c | 45 +++++--- fs/afs/fs_operation.c | 11 +- fs/afs/fsclient.c | 7 +- fs/afs/inode.c | 59 +++++------ fs/afs/internal.h | 78 ++++---------- fs/afs/proc.c | 15 ++- fs/afs/rotate.c | 128 ++++------------------ fs/afs/rxrpc.c | 5 +- fs/afs/security.c | 8 +- fs/afs/server.c | 13 +-- fs/afs/server_list.c | 30 +----- fs/afs/super.c | 8 +- fs/afs/vl_alias.c | 14 +-- fs/afs/volume.c | 84 +++++++++++++-- fs/afs/yfsclient.c | 2 +- 17 files changed, 257 insertions(+), 543 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 282dbac84435..b4cb9bb63f0a 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -20,185 +20,6 @@ #include #include "internal.h" -/* - * Create volume and callback interests on a server. - */ -static struct afs_cb_interest *afs_create_interest(struct afs_server *server, - struct afs_vnode *vnode) -{ - struct afs_vol_interest *new_vi, *vi; - struct afs_cb_interest *new; - struct rb_node *parent, **pp; - - new_vi = kzalloc(sizeof(struct afs_vol_interest), GFP_KERNEL); - if (!new_vi) - return NULL; - - new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL); - if (!new) { - kfree(new_vi); - return NULL; - } - - new_vi->usage = 1; - new_vi->vid = vnode->volume->vid; - INIT_HLIST_HEAD(&new_vi->cb_interests); - - refcount_set(&new->usage, 1); - new->sb = vnode->vfs_inode.i_sb; - new->server = afs_get_server(server, afs_server_trace_get_new_cbi); - INIT_HLIST_NODE(&new->cb_vlink); - - write_seqlock(&server->cb_break_lock); - - pp = &server->cb_volumes.rb_node; - while ((parent = *pp)) { - vi = rb_entry(parent, struct afs_vol_interest, srv_node); - if (vi->vid < new_vi->vid) { - pp = &(*pp)->rb_left; - } else if (vi->vid > new_vi->vid) { - pp = &(*pp)->rb_right; - } else { - vi->usage++; - goto found_vi; - } - } - - vi = new_vi; - new_vi = NULL; - rb_link_node_rcu(&vi->srv_node, parent, pp); - rb_insert_color(&vi->srv_node, &server->cb_volumes); - -found_vi: - new->vol_interest = vi; - hlist_add_head(&new->cb_vlink, &vi->cb_interests); - - write_sequnlock(&server->cb_break_lock); - kfree(new_vi); - return new; -} - -/* - * Set up an interest-in-callbacks record for a volume on a server and - * register it with the server. - * - Called with vnode->io_lock held. - */ -int afs_register_server_cb_interest(struct afs_vnode *vnode, - struct afs_server_list *slist, - unsigned int index) -{ - struct afs_server_entry *entry = &slist->servers[index]; - struct afs_cb_interest *cbi, *vcbi, *new, *old; - struct afs_server *server = entry->server; - -again: - 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); - - if (vcbi) { - if (vcbi == cbi) { - afs_put_cb_interest(afs_v2net(vnode), cbi); - return 0; - } - - /* Use a new interest in the server list for the same server - * rather than an old one that's still attached to a vnode. - */ - if (cbi && vcbi->server == cbi->server) { - write_seqlock(&vnode->cb_lock); - 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; - } - - /* Re-use the one attached to the vnode. */ - if (!cbi && vcbi->server == server) { - write_lock(&slist->lock); - if (entry->cb_interest) { - write_unlock(&slist->lock); - afs_put_cb_interest(afs_v2net(vnode), cbi); - goto again; - } - - entry->cb_interest = cbi; - write_unlock(&slist->lock); - return 0; - } - } - - if (!cbi) { - new = afs_create_interest(server, vnode); - if (!new) - return -ENOMEM; - - write_lock(&slist->lock); - if (!entry->cb_interest) { - entry->cb_interest = afs_get_cb_interest(new); - cbi = new; - new = NULL; - } else { - cbi = afs_get_cb_interest(entry->cb_interest); - } - write_unlock(&slist->lock); - afs_put_cb_interest(afs_v2net(vnode), new); - } - - ASSERT(cbi); - - /* Change the server the vnode is using. This entails scrubbing any - * interest the vnode had in the previous server it was using. - */ - write_seqlock(&vnode->cb_lock); - - 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); - - write_sequnlock(&vnode->cb_lock); - afs_put_cb_interest(afs_v2net(vnode), old); - return 0; -} - -/* - * Remove an interest on a server. - */ -void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) -{ - struct afs_vol_interest *vi; - - if (cbi && refcount_dec_and_test(&cbi->usage)) { - if (!hlist_unhashed(&cbi->cb_vlink)) { - write_seqlock(&cbi->server->cb_break_lock); - - hlist_del_init(&cbi->cb_vlink); - vi = cbi->vol_interest; - cbi->vol_interest = NULL; - if (--vi->usage == 0) - rb_erase(&vi->srv_node, &cbi->server->cb_volumes); - else - vi = NULL; - - write_sequnlock(&cbi->server->cb_break_lock); - if (vi) - kfree_rcu(vi, rcu); - afs_put_server(net, cbi->server, afs_server_trace_put_cbi); - } - kfree_rcu(cbi, rcu); - } -} - /* * allow the fileserver to request callback state (re-)initialisation */ @@ -236,12 +57,12 @@ void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason } /* - * Look up a volume interest by volume ID under RCU conditions. + * Look up a volume by volume ID under RCU conditions. */ -static struct afs_vol_interest *afs_lookup_vol_interest_rcu(struct afs_server *server, - afs_volid_t vid) +static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, + afs_volid_t vid) { - struct afs_vol_interest *vi = NULL; + struct afs_volume *volume = NULL; struct rb_node *p; int seq = 0; @@ -250,28 +71,25 @@ static struct afs_vol_interest *afs_lookup_vol_interest_rcu(struct afs_server *s * under just the RCU read lock, so we have to check for * changes. */ - read_seqbegin_or_lock(&server->cb_break_lock, &seq); + read_seqbegin_or_lock(&cell->volume_lock, &seq); - p = rcu_dereference_raw(server->cb_volumes.rb_node); + p = rcu_dereference_raw(cell->volumes.rb_node); while (p) { - vi = rb_entry(p, struct afs_vol_interest, srv_node); + volume = rb_entry(p, struct afs_volume, cell_node); - if (vi->vid < vid) + if (volume->vid < vid) p = rcu_dereference_raw(p->rb_left); - else if (vi->vid > vid) + else if (volume->vid > vid) p = rcu_dereference_raw(p->rb_right); else break; - /* We want to repeat the search, this time with the - * lock properly locked. - */ - vi = NULL; + volume = NULL; } - } while (need_seqretry(&server->cb_break_lock, seq)); + } while (need_seqretry(&cell->volume_lock, seq)); - done_seqretry(&server->cb_break_lock, seq); - return vi; + done_seqretry(&cell->volume_lock, seq); + return volume; } /* @@ -280,42 +98,37 @@ static struct afs_vol_interest *afs_lookup_vol_interest_rcu(struct afs_server *s * - the backing file is changed * - a lock is released */ -static void afs_break_one_callback(struct afs_server *server, - struct afs_fid *fid, - struct afs_vol_interest *vi) +static void afs_break_one_callback(struct afs_volume *volume, + struct afs_fid *fid) { - struct afs_cb_interest *cbi; + struct super_block *sb; struct afs_vnode *vnode; struct inode *inode; - /* Step through all interested superblocks. There may be more than one - * because of cell aliasing. - */ - hlist_for_each_entry_rcu(cbi, &vi->cb_interests, cb_vlink) { - if (fid->vnode == 0 && fid->unique == 0) { - /* The callback break applies to an entire volume. */ - struct afs_super_info *as = AFS_FS_S(cbi->sb); - struct afs_volume *volume = as->volume; + if (fid->vnode == 0 && fid->unique == 0) { + /* The callback break applies to an entire volume. */ + write_lock(&volume->cb_v_break_lock); + volume->cb_v_break++; + trace_afs_cb_break(fid, volume->cb_v_break, + afs_cb_break_for_volume_callback, false); + write_unlock(&volume->cb_v_break_lock); + return; + } - write_lock(&volume->cb_v_break_lock); - volume->cb_v_break++; - trace_afs_cb_break(fid, volume->cb_v_break, - afs_cb_break_for_volume_callback, false); - write_unlock(&volume->cb_v_break_lock); - } else { - /* See if we can find a matching inode - even an I_NEW - * inode needs to be marked as it can have its callback - * broken before we finish setting up the local inode. - */ - inode = find_inode_rcu(cbi->sb, fid->vnode, - afs_ilookup5_test_by_fid, fid); - if (inode) { - vnode = AFS_FS_I(inode); - afs_break_callback(vnode, afs_cb_break_for_callback); - } else { - trace_afs_cb_miss(fid, afs_cb_break_for_callback); - } - } + /* See if we can find a matching inode - even an I_NEW inode needs to + * be marked as it can have its callback broken before we finish + * setting up the local inode. + */ + sb = rcu_dereference(volume->sb); + if (!sb) + return; + + inode = find_inode_rcu(sb, fid->vnode, afs_ilookup5_test_by_fid, fid); + if (inode) { + vnode = AFS_FS_I(inode); + afs_break_callback(vnode, afs_cb_break_for_callback); + } else { + trace_afs_cb_miss(fid, afs_cb_break_for_callback); } } @@ -324,11 +137,11 @@ static void afs_break_some_callbacks(struct afs_server *server, size_t *_count) { struct afs_callback_break *residue = cbb; - struct afs_vol_interest *vi; + struct afs_volume *volume; afs_volid_t vid = cbb->fid.vid; size_t i; - vi = afs_lookup_vol_interest_rcu(server, vid); + volume = afs_lookup_volume_rcu(server->cell, vid); /* TODO: Find all matching volumes if we couldn't match the server and * break them anyway. @@ -341,8 +154,8 @@ static void afs_break_some_callbacks(struct afs_server *server, cbb->fid.vnode, cbb->fid.unique); --*_count; - if (vi) - afs_break_one_callback(server, &cbb->fid, vi); + if (volume) + afs_break_one_callback(volume, &cbb->fid); } else { *residue++ = *cbb; } @@ -367,16 +180,3 @@ void afs_break_callbacks(struct afs_server *server, size_t count, rcu_read_unlock(); return; } - -/* - * Clear the callback interests in a server list. - */ -void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist) -{ - int i; - - for (i = 0; i < slist->nr_servers; i++) { - afs_put_cb_interest(net, slist->servers[i].cb_interest); - slist->servers[i].cb_interest = NULL; - } -} diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 8bfc8a05fd46..005921e3b38d 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -161,8 +161,11 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, atomic_set(&cell->usage, 2); INIT_WORK(&cell->manager, afs_manage_cell); - INIT_LIST_HEAD(&cell->proc_volumes); - rwlock_init(&cell->proc_lock); + cell->volumes = RB_ROOT; + INIT_HLIST_HEAD(&cell->proc_volumes); + seqlock_init(&cell->volume_lock); + cell->fs_servers = RB_ROOT; + seqlock_init(&cell->fs_lock); rwlock_init(&cell->vl_servers_lock); cell->flags = (1 << AFS_CELL_FL_CHECK_ALIAS); diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 0d3cf3af0352..25cbe0aeeec5 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -702,6 +702,37 @@ static const struct afs_operation_ops afs_fetch_status_operation = { .success = afs_do_lookup_success, }; +/* + * See if we know that the server we expect to use doesn't support + * FS.InlineBulkStatus. + */ +static bool afs_server_supports_ibulk(struct afs_vnode *dvnode) +{ + struct afs_server_list *slist; + struct afs_volume *volume = dvnode->volume; + struct afs_server *server; + bool ret = true; + int i; + + if (!test_bit(AFS_VOLUME_MAYBE_NO_IBULK, &volume->flags)) + return true; + + rcu_read_lock(); + slist = rcu_dereference(volume->servers); + + for (i = 0; i < slist->nr_servers; i++) { + server = slist->servers[i].server; + if (server == dvnode->cb_server) { + if (test_bit(AFS_SERVER_FL_NO_IBULK, &server->flags)) + ret = false; + break; + } + } + + rcu_read_unlock(); + return ret; +} + /* * Do a lookup in a directory. We make use of bulk lookup to query a slew of * files in one go and create inodes for them. The inode of the file we were @@ -711,10 +742,8 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, struct key *key) { struct afs_lookup_cookie *cookie; - struct afs_cb_interest *dcbi; struct afs_vnode_param *vp; struct afs_operation *op; - struct afs_server *server; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; struct inode *inode = NULL, *ti; afs_dataversion_t data_version = READ_ONCE(dvnode->status.data_version); @@ -734,16 +763,8 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, cookie->nr_fids = 2; /* slot 0 is saved for the fid we actually want * and slot 1 for the directory */ - read_seqlock_excl(&dvnode->cb_lock); - dcbi = rcu_dereference_protected(dvnode->cb_interest, - lockdep_is_held(&dvnode->cb_lock.lock)); - if (dcbi) { - server = dcbi->server; - if (server && - test_bit(AFS_SERVER_FL_NO_IBULK, &server->flags)) - cookie->one_only = true; - } - read_sequnlock_excl(&dvnode->cb_lock); + if (!afs_server_supports_ibulk(dvnode)) + cookie->one_only = true; /* search the directory */ ret = afs_dir_iterate(dir, &cookie->ctx, key, &data_version); diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c index f57efd9d2db0..2d2dff5688a4 100644 --- a/fs/afs/fs_operation.c +++ b/fs/afs/fs_operation.c @@ -143,12 +143,6 @@ bool afs_begin_vnode_operation(struct afs_operation *op) if (!afs_get_io_locks(op)) return false; - read_seqlock_excl(&vnode->cb_lock); - op->cbi = afs_get_cb_interest( - rcu_dereference_protected(vnode->cb_interest, - lockdep_is_held(&vnode->cb_lock.lock))); - read_sequnlock_excl(&vnode->cb_lock); - afs_prepare_vnode(op, &op->file[0], 0); afs_prepare_vnode(op, &op->file[1], 1); op->cb_v_break = op->volume->cb_v_break; @@ -183,8 +177,8 @@ void afs_wait_for_operation(struct afs_operation *op) _enter(""); while (afs_select_fileserver(op)) { - op->cb_s_break = op->cbi->server->cb_s_break; - if (test_bit(AFS_SERVER_FL_IS_YFS, &op->cbi->server->flags) && + op->cb_s_break = op->server->cb_s_break; + if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) && op->ops->issue_yfs_rpc) op->ops->issue_yfs_rpc(op); else @@ -231,7 +225,6 @@ int afs_put_operation(struct afs_operation *op) } afs_end_cursor(&op->ac); - afs_put_cb_interest(op->net, op->cbi); afs_put_serverlist(op->net, op->server_list); afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op); kfree(op); diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index b1b45f10583d..acb4d0ca2649 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -1893,8 +1893,11 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) static void afs_done_fs_inline_bulk_status(struct afs_call *call) { if (call->error == -ECONNABORTED && - call->abort_code == RX_INVALID_OPERATION) + call->abort_code == RX_INVALID_OPERATION) { set_bit(AFS_SERVER_FL_NO_IBULK, &call->server->flags); + if (call->op) + set_bit(AFS_VOLUME_MAYBE_NO_IBULK, &call->op->volume->flags); + } } /* @@ -1919,7 +1922,7 @@ void afs_fs_inline_bulk_status(struct afs_operation *op) __be32 *bp; int i; - if (test_bit(AFS_SERVER_FL_NO_IBULK, &op->cbi->server->flags)) { + if (test_bit(AFS_SERVER_FL_NO_IBULK, &op->server->flags)) { op->error = -ENOTSUPP; return; } diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 94675acb6a3a..7dde703df40c 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -71,7 +71,6 @@ static int afs_inode_init_from_status(struct afs_operation *op, struct afs_vnode_param *vp, struct afs_vnode *vnode) { - struct afs_cb_interest *old_cbi = NULL; struct afs_file_status *status = &vp->scb.status; struct inode *inode = AFS_VNODE_TO_I(vnode); struct timespec64 t; @@ -150,18 +149,11 @@ static int afs_inode_init_from_status(struct afs_operation *op, vnode->cb_expires_at = ktime_get_real_seconds(); } else { vnode->cb_expires_at = vp->scb.callback.expires_at; - old_cbi = rcu_dereference_protected(vnode->cb_interest, - lockdep_is_held(&vnode->cb_lock.lock)); - if (op->cbi != old_cbi) - rcu_assign_pointer(vnode->cb_interest, - afs_get_cb_interest(op->cbi)); - else - old_cbi = NULL; + vnode->cb_server = op->server; set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); } write_sequnlock(&vnode->cb_lock); - afs_put_cb_interest(afs_v2net(vnode), old_cbi); return 0; } @@ -255,18 +247,12 @@ static void afs_apply_status(struct afs_operation *op, static void afs_apply_callback(struct afs_operation *op, struct afs_vnode_param *vp) { - struct afs_cb_interest *old; struct afs_callback *cb = &vp->scb.callback; struct afs_vnode *vnode = vp->vnode; - if (!afs_cb_is_broken(vp->cb_break_before, vnode, op->cbi)) { + if (!afs_cb_is_broken(vp->cb_break_before, vnode)) { vnode->cb_expires_at = cb->expires_at; - old = rcu_dereference_protected(vnode->cb_interest, - lockdep_is_held(&vnode->cb_lock.lock)); - if (old != op->cbi) { - rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(op->cbi)); - afs_put_cb_interest(afs_v2net(vnode), old); - } + vnode->cb_server = op->server; set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); } } @@ -569,13 +555,31 @@ void afs_zap_data(struct afs_vnode *vnode) invalidate_inode_pages2(vnode->vfs_inode.i_mapping); } +/* + * Get the server reinit counter for a vnode's current server. + */ +static bool afs_get_s_break_rcu(struct afs_vnode *vnode, unsigned int *_s_break) +{ + struct afs_server_list *slist = rcu_dereference(vnode->volume->servers); + struct afs_server *server; + int i; + + for (i = 0; i < slist->nr_servers; i++) { + server = slist->servers[i].server; + if (server == vnode->cb_server) { + *_s_break = READ_ONCE(server->cb_s_break); + return true; + } + } + + return false; +} + /* * Check the validity of a vnode/inode. */ bool afs_check_validity(struct afs_vnode *vnode) { - struct afs_cb_interest *cbi; - struct afs_server *server; struct afs_volume *volume = vnode->volume; enum afs_cb_break_reason need_clear = afs_cb_break_no_break; time64_t now = ktime_get_real_seconds(); @@ -588,11 +592,8 @@ bool afs_check_validity(struct afs_vnode *vnode) cb_v_break = READ_ONCE(volume->cb_v_break); cb_break = vnode->cb_break; - if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { - cbi = rcu_dereference(vnode->cb_interest); - server = rcu_dereference(cbi->server); - cb_s_break = READ_ONCE(server->cb_s_break); - + if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags) && + afs_get_s_break_rcu(vnode, &cb_s_break)) { if (vnode->cb_s_break != cb_s_break || vnode->cb_v_break != cb_v_break) { vnode->cb_s_break = cb_s_break; @@ -739,7 +740,6 @@ int afs_drop_inode(struct inode *inode) */ void afs_evict_inode(struct inode *inode) { - struct afs_cb_interest *cbi; struct afs_vnode *vnode; vnode = AFS_FS_I(inode); @@ -756,15 +756,6 @@ void afs_evict_inode(struct inode *inode) truncate_inode_pages_final(&inode->i_data); clear_inode(inode); - write_seqlock(&vnode->cb_lock); - cbi = rcu_dereference_protected(vnode->cb_interest, - lockdep_is_held(&vnode->cb_lock.lock)); - if (cbi) { - afs_put_cb_interest(afs_i2net(inode), cbi); - rcu_assign_pointer(vnode->cb_interest, NULL); - } - write_sequnlock(&vnode->cb_lock); - while (!list_empty(&vnode->wb_keys)) { struct afs_wb_key *wbk = list_entry(vnode->wb_keys.next, struct afs_wb_key, vnode_link); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index e084936066b0..c64c2b47ece7 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -103,7 +103,6 @@ struct afs_call { struct afs_net *net; /* The network namespace */ struct afs_server *server; /* The fileserver record if fs op (pins ref) */ struct afs_vlserver *vlserver; /* The vlserver record if vl op */ - struct afs_cb_interest *cbi; /* Callback interest for server used */ void *request; /* request data (first part) */ struct iov_iter def_iter; /* Default buffer/data iterator */ struct iov_iter *iter; /* Iterator currently in use */ @@ -375,9 +374,14 @@ struct afs_cell { enum dns_lookup_status dns_status:8; /* Latest status of data from lookup */ unsigned int dns_lookup_count; /* Counter of DNS lookups */ + /* The volumes belonging to this cell */ + struct rb_root volumes; /* Tree of volumes on this server */ + struct hlist_head proc_volumes; /* procfs volume list */ + seqlock_t volume_lock; /* For volumes */ + /* Active fileserver interaction state. */ - struct list_head proc_volumes; /* procfs volume list */ - rwlock_t proc_lock; + struct rb_root fs_servers; /* afs_server (by server UUID) */ + seqlock_t fs_lock; /* For fs_servers */ /* VL server list. */ rwlock_t vl_servers_lock; /* Lock on vl_servers */ @@ -481,7 +485,8 @@ struct afs_server { }; struct afs_addr_list __rcu *addresses; - struct rb_node uuid_rb; /* Link in net->fs_servers */ + struct afs_cell *cell; /* Cell to which belongs (pins ref) */ + struct rb_node uuid_rb; /* Link in cell->fs_servers */ struct list_head probe_link; /* Link in net->fs_probe_list */ struct hlist_node addr4_link; /* Link in net->fs_addresses4 */ struct hlist_node addr6_link; /* Link in net->fs_addresses6 */ @@ -507,9 +512,7 @@ struct afs_server { rwlock_t fs_lock; /* access lock */ /* callback promise management */ - struct rb_root cb_volumes; /* List of volume interests on this server */ unsigned cb_s_break; /* Break-everything counter. */ - seqlock_t cb_break_lock; /* Volume finding lock */ /* Probe state */ unsigned long probed_at; /* Time last probe was dispatched (jiffies) */ @@ -527,37 +530,11 @@ struct afs_server { } probe; }; -/* - * Volume collation in the server's callback interest list. - */ -struct afs_vol_interest { - struct rb_node srv_node; /* Link in server->cb_volumes */ - struct hlist_head cb_interests; /* List of callback interests on the server */ - union { - struct rcu_head rcu; - afs_volid_t vid; /* Volume ID to match */ - }; - unsigned int usage; -}; - -/* - * Interest by a superblock on a server. - */ -struct afs_cb_interest { - struct hlist_node cb_vlink; /* Link in vol_interest->cb_interests */ - struct afs_vol_interest *vol_interest; - struct afs_server *server; /* Server on which this interest resides */ - struct super_block *sb; /* Superblock on which inodes reside */ - struct rcu_head rcu; - refcount_t usage; -}; - /* * Replaceable volume server list. */ struct afs_server_entry { struct afs_server *server; - struct afs_cb_interest *cb_interest; }; struct afs_server_list { @@ -575,11 +552,16 @@ struct afs_server_list { * Live AFS volume management. */ struct afs_volume { - afs_volid_t vid; /* volume ID */ + union { + struct rcu_head rcu; + afs_volid_t vid; /* volume ID */ + }; atomic_t usage; time64_t update_at; /* Time at which to next update */ struct afs_cell *cell; /* Cell to which belongs (pins ref) */ - struct list_head proc_link; /* Link in cell->vl_proc */ + struct rb_node cell_node; /* Link in cell->volumes */ + struct hlist_node proc_link; /* Link in cell->proc_volumes */ + struct super_block __rcu *sb; /* Superblock on which inodes reside */ unsigned long flags; #define AFS_VOLUME_NEEDS_UPDATE 0 /* - T if an update needs performing */ #define AFS_VOLUME_UPDATING 1 /* - T if an update is in progress */ @@ -587,6 +569,7 @@ struct afs_volume { #define AFS_VOLUME_DELETED 3 /* - T if volume appears deleted */ #define AFS_VOLUME_OFFLINE 4 /* - T if volume offline notice given */ #define AFS_VOLUME_BUSY 5 /* - T if volume busy notice given */ +#define AFS_VOLUME_MAYBE_NO_IBULK 6 /* - T if some servers don't have InlineBulkStatus */ #ifdef CONFIG_AFS_FSCACHE struct fscache_cookie *cache; /* caching cookie */ #endif @@ -598,7 +581,6 @@ struct afs_volume { rwlock_t cb_v_break_lock; afs_voltype_t type; /* type of volume */ - short error; char type_force; /* force volume type (suppress R/O -> R/W) */ u8 name_len; u8 name[AFS_MAXVOLNAME + 1]; /* NUL-padded volume name */ @@ -659,11 +641,11 @@ struct afs_vnode { afs_lock_type_t lock_type : 8; /* outstanding callback notification on this file */ - struct afs_cb_interest __rcu *cb_interest; /* Server on which this resides */ + void *cb_server; /* Server with callback/filelock */ unsigned int cb_s_break; /* Mass break counter on ->server */ unsigned int cb_v_break; /* Mass break counter on ->volume */ unsigned int cb_break; /* Break counter on vnode */ - seqlock_t cb_lock; /* Lock for ->cb_interest, ->status, ->cb_*break */ + seqlock_t cb_lock; /* Lock for ->cb_server, ->status, ->cb_*break */ time64_t cb_expires_at; /* time at which callback expires */ }; @@ -833,7 +815,7 @@ struct afs_operation { /* Fileserver iteration state */ struct afs_addr_cursor ac; struct afs_server_list *server_list; /* Current server list (pins ref) */ - struct afs_cb_interest *cbi; /* Server on which this resides (pins ref) */ + struct afs_server *server; /* Server we're using (ref pinned by server_list) */ struct afs_call *call; unsigned long untried; /* Bitmask of untried servers */ short index; /* Current server */ @@ -907,29 +889,15 @@ extern void __afs_break_callback(struct afs_vnode *, enum afs_cb_break_reason); extern void afs_break_callback(struct afs_vnode *, enum afs_cb_break_reason); extern void afs_break_callbacks(struct afs_server *, size_t, struct afs_callback_break *); -extern int afs_register_server_cb_interest(struct afs_vnode *, - struct afs_server_list *, unsigned int); -extern void afs_put_cb_interest(struct afs_net *, struct afs_cb_interest *); -extern void afs_clear_callback_interests(struct afs_net *, struct afs_server_list *); - -static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest *cbi) -{ - if (cbi) - refcount_inc(&cbi->usage); - return cbi; -} - static inline unsigned int afs_calc_vnode_cb_break(struct afs_vnode *vnode) { return vnode->cb_break + vnode->cb_v_break; } static inline bool afs_cb_is_broken(unsigned int cb_break, - const struct afs_vnode *vnode, - const struct afs_cb_interest *cbi) + const struct afs_vnode *vnode) { - return !cbi || cb_break != (vnode->cb_break + - vnode->volume->cb_v_break); + return cb_break != (vnode->cb_break + vnode->volume->cb_v_break); } /* @@ -1182,7 +1150,6 @@ static inline void afs_put_sysnames(struct afs_sysnames *sysnames) {} * rotate.c */ extern bool afs_select_fileserver(struct afs_operation *); -extern bool afs_select_current_fileserver(struct afs_operation *); extern void afs_dump_edestaddrreq(const struct afs_operation *); /* @@ -1212,7 +1179,6 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c op->type = call->type; call->op = op; call->key = op->key; - call->cbi = afs_get_cb_interest(op->cbi); call->intr = !(op->flags & AFS_OPERATION_UNINTR); afs_make_call(&op->ac, call, gfp); } diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 256c3eff8c82..309a7b578255 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -209,11 +209,10 @@ static const char afs_vol_types[3][3] = { */ static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) { - struct afs_cell *cell = PDE_DATA(file_inode(m->file)); - struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link); + struct afs_volume *vol = hlist_entry(v, struct afs_volume, proc_link); /* Display header on line 1 */ - if (v == &cell->proc_volumes) { + if (v == SEQ_START_TOKEN) { seq_puts(m, "USE VID TY NAME\n"); return 0; } @@ -231,8 +230,8 @@ static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) { struct afs_cell *cell = PDE_DATA(file_inode(m->file)); - read_lock(&cell->proc_lock); - return seq_list_start_head(&cell->proc_volumes, *_pos); + rcu_read_lock(); + return seq_hlist_start_head_rcu(&cell->proc_volumes, *_pos); } static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v, @@ -240,15 +239,13 @@ static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v, { struct afs_cell *cell = PDE_DATA(file_inode(m->file)); - return seq_list_next(v, &cell->proc_volumes, _pos); + return seq_hlist_next_rcu(v, &cell->proc_volumes, _pos); } static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v) __releases(cell->proc_lock) { - struct afs_cell *cell = PDE_DATA(file_inode(m->file)); - - read_unlock(&cell->proc_lock); + rcu_read_unlock(); } static const struct seq_operations afs_proc_cell_volumes_ops = { diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index d67b8c7eb3b9..8d5473cd8ea4 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -21,7 +21,8 @@ static bool afs_start_fs_iteration(struct afs_operation *op, struct afs_vnode *vnode) { - struct afs_cb_interest *cbi; + struct afs_server *server; + void *cb_server; int i; read_lock(&op->volume->servers_lock); @@ -33,12 +34,12 @@ static bool afs_start_fs_iteration(struct afs_operation *op, op->untried = (1UL << op->server_list->nr_servers) - 1; op->index = READ_ONCE(op->server_list->preferred); - cbi = rcu_dereference_protected(vnode->cb_interest, - lockdep_is_held(&vnode->io_lock)); - if (cbi) { + cb_server = vnode->cb_server; + if (cb_server) { /* See if the vnode's preferred record is still available */ for (i = 0; i < op->server_list->nr_servers; i++) { - if (op->server_list->servers[i].cb_interest == cbi) { + server = op->server_list->servers[i].server; + if (server == cb_server) { op->index = i; goto found_interest; } @@ -55,14 +56,11 @@ static bool afs_start_fs_iteration(struct afs_operation *op, /* Note that the callback promise is effectively broken */ write_seqlock(&vnode->cb_lock); - ASSERTCMP(cbi, ==, rcu_access_pointer(vnode->cb_interest)); - rcu_assign_pointer(vnode->cb_interest, NULL); + ASSERTCMP(cb_server, ==, vnode->cb_server); + vnode->cb_server = NULL; if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) vnode->cb_break++; write_sequnlock(&vnode->cb_lock); - - afs_put_cb_interest(op->net, cbi); - cbi = NULL; } found_interest: @@ -303,8 +301,7 @@ bool afs_select_fileserver(struct afs_operation *op) restart_from_beginning: _debug("restart"); afs_end_cursor(&op->ac); - afs_put_cb_interest(op->net, op->cbi); - op->cbi = NULL; + op->server = NULL; afs_put_serverlist(op->net, op->server_list); op->server_list = NULL; start: @@ -331,13 +328,12 @@ pick_server: /* Pick the untried server with the lowest RTT. If we have outstanding * callbacks, we stick with the server we're already using if we can. */ - if (op->cbi) { - _debug("cbi %u", op->index); + if (op->server) { + _debug("server %u", op->index); if (test_bit(op->index, &op->untried)) goto selected_server; - afs_put_cb_interest(op->net, op->cbi); - op->cbi = NULL; - _debug("nocbi"); + op->server = NULL; + _debug("no server"); } op->index = -1; @@ -372,19 +368,13 @@ selected_server: _debug("USING SERVER: %pU", &server->uuid); - /* Make sure we've got a callback interest record for this server. We - * have to link it in before we send the request as we can be sent a - * break request before we've finished decoding the reply and - * installing the vnode. - */ - error = afs_register_server_cb_interest(vnode, op->server_list, - op->index); - if (error < 0) - goto failed_set_error; - - op->cbi = afs_get_cb_interest( - rcu_dereference_protected(vnode->cb_interest, - lockdep_is_held(&vnode->io_lock))); + op->server = server; + if (vnode->cb_server != server) { + vnode->cb_server = server; + vnode->cb_s_break = server->cb_s_break; + vnode->cb_v_break = vnode->volume->cb_v_break; + clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); + } read_lock(&server->fs_lock); alist = rcu_dereference_protected(server->addresses, @@ -446,84 +436,6 @@ failed: return false; } -/* - * Select the same fileserver we used for a vnode before and only that - * fileserver. We use this when we have a lock on that file, which is backed - * only by the fileserver we obtained it from. - */ -bool afs_select_current_fileserver(struct afs_operation *op) -{ - struct afs_cb_interest *cbi; - struct afs_addr_list *alist; - int error = op->ac.error; - - _enter(""); - - switch (error) { - case SHRT_MAX: - cbi = op->cbi; - if (!cbi) { - op->error = -ESTALE; - op->flags |= AFS_OPERATION_STOP; - return false; - } - - 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) { - op->error = -ESTALE; - op->flags |= AFS_OPERATION_STOP; - return false; - } - - memset(&op->ac, 0, sizeof(op->ac)); - op->ac.alist = alist; - op->ac.index = -1; - goto iterate_address; - - case 0: - default: - /* Success or local failure. Stop. */ - op->error = error; - op->flags |= AFS_OPERATION_STOP; - _leave(" = f [okay/local %d]", error); - return false; - - case -ECONNABORTED: - op->error = afs_abort_to_error(op->ac.abort_code); - op->flags |= AFS_OPERATION_STOP; - _leave(" = f [abort]"); - return false; - - case -ERFKILL: - case -EADDRNOTAVAIL: - case -ENETUNREACH: - case -EHOSTUNREACH: - case -EHOSTDOWN: - case -ECONNREFUSED: - case -ETIMEDOUT: - case -ETIME: - _debug("no conn"); - op->error = error; - goto iterate_address; - } - -iterate_address: - /* Iterate over the current server's address list to try and find an - * address on which it will respond to us. - */ - if (afs_iterate_addresses(&op->ac)) { - _leave(" = t"); - return true; - } - - afs_end_cursor(&op->ac); - return false; -} - /* * Dump cursor state in the case of the error being EDESTADDRREQ. */ diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index bd4d8e5efe59..b7fb5f98f80c 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -184,7 +184,6 @@ void afs_put_call(struct afs_call *call) call->type->destructor(call); afs_unuse_server_notime(call->net, call->server, afs_server_trace_put_call); - afs_put_cb_interest(call->net, call->cbi); afs_put_addrlist(call->alist); kfree(call->request); @@ -550,9 +549,9 @@ static void afs_deliver_to_call(struct afs_call *call) case 0: afs_queue_call_work(call); if (state == AFS_CALL_CL_PROC_REPLY) { - if (call->cbi) + if (call->op) set_bit(AFS_SERVER_FL_MAY_HAVE_CB, - &call->cbi->server->flags); + &call->op->server->flags); goto call_complete; } ASSERTCMP(state, >, AFS_CALL_CL_PROC_REPLY); diff --git a/fs/afs/security.c b/fs/afs/security.c index ce9de1e6742b..90d852704328 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -170,8 +170,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, break; } - if (afs_cb_is_broken(cb_break, vnode, - rcu_dereference(vnode->cb_interest))) { + if (afs_cb_is_broken(cb_break, vnode)) { changed = true; break; } @@ -201,7 +200,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, } } - if (afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest))) + if (afs_cb_is_broken(cb_break, vnode)) goto someone_else_changed_it; /* We need a ref on any permits list we want to copy as we'll have to @@ -281,8 +280,7 @@ found: rcu_read_lock(); spin_lock(&vnode->lock); zap = rcu_access_pointer(vnode->permit_cache); - if (!afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)) && - zap == permits) + if (!afs_cb_is_broken(cb_break, vnode) && zap == permits) rcu_assign_pointer(vnode->permit_cache, replacement); else zap = replacement; diff --git a/fs/afs/server.c b/fs/afs/server.c index 1c1e315094ae..c51039a077cd 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -132,11 +132,12 @@ struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uu /* * Install a server record in the namespace tree */ -static struct afs_server *afs_install_server(struct afs_net *net, +static struct afs_server *afs_install_server(struct afs_cell *cell, struct afs_server *candidate) { const struct afs_addr_list *alist; struct afs_server *server; + struct afs_net *net = cell->net; struct rb_node **pp, *p; int diff; @@ -193,11 +194,12 @@ exists: /* * Allocate a new server record and mark it active. */ -static struct afs_server *afs_alloc_server(struct afs_net *net, +static struct afs_server *afs_alloc_server(struct afs_cell *cell, const uuid_t *uuid, struct afs_addr_list *alist) { struct afs_server *server; + struct afs_net *net = cell->net; _enter(""); @@ -212,11 +214,10 @@ static struct afs_server *afs_alloc_server(struct afs_net *net, server->addr_version = alist->version; server->uuid = *uuid; rwlock_init(&server->fs_lock); - server->cb_volumes = RB_ROOT; - seqlock_init(&server->cb_break_lock); init_waitqueue_head(&server->probe_wq); INIT_LIST_HEAD(&server->probe_link); spin_lock_init(&server->probe_lock); + server->cell = cell; afs_inc_servers_outstanding(net); trace_afs_server(server, 1, 1, afs_server_trace_alloc); @@ -275,13 +276,13 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key, if (IS_ERR(alist)) return ERR_CAST(alist); - candidate = afs_alloc_server(cell->net, uuid, alist); + candidate = afs_alloc_server(cell, uuid, alist); if (!candidate) { afs_put_addrlist(alist); return ERR_PTR(-ENOMEM); } - server = afs_install_server(cell->net, candidate); + server = afs_install_server(cell, candidate); if (server != candidate) { afs_put_addrlist(alist); kfree(candidate); diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index a35f6951a74a..ed9056703505 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -14,11 +14,9 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist) int i; if (slist && refcount_dec_and_test(&slist->usage)) { - for (i = 0; i < slist->nr_servers; i++) { - afs_put_cb_interest(net, slist->servers[i].cb_interest); + for (i = 0; i < slist->nr_servers; i++) afs_unuse_server(net, slist->servers[i].server, afs_server_trace_put_slist); - } kfree(slist); } } @@ -127,31 +125,5 @@ changed: } } - /* Keep the old callback interest records where possible so that we - * maintain callback interception. - */ - i = 0; - j = 0; - while (i < old->nr_servers && j < new->nr_servers) { - if (new->servers[j].server == old->servers[i].server) { - struct afs_cb_interest *cbi = old->servers[i].cb_interest; - if (cbi) { - new->servers[j].cb_interest = cbi; - refcount_inc(&cbi->usage); - } - i++; - j++; - continue; - } - - if (new->servers[j].server < old->servers[i].server) { - j++; - continue; - } - - i++; - continue; - } - return true; } diff --git a/fs/afs/super.c b/fs/afs/super.c index f92ccdafc729..c77b11b31233 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -485,6 +485,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx) goto error; } else { sb->s_d_op = &afs_fs_dentry_operations; + rcu_assign_pointer(as->volume->sb, sb); } _leave(" = 0"); @@ -529,7 +530,6 @@ static void afs_destroy_sbi(struct afs_super_info *as) static void afs_kill_super(struct super_block *sb) { struct afs_super_info *as = AFS_FS_S(sb); - struct afs_net *net = afs_net(as->net_ns); if (as->dyn_root) afs_dynroot_depopulate(sb); @@ -538,8 +538,7 @@ static void afs_kill_super(struct super_block *sb) * deactivating the superblock. */ if (as->volume) - afs_clear_callback_interests( - net, rcu_access_pointer(as->volume->servers)); + rcu_assign_pointer(as->volume->sb, NULL); kill_anon_super(sb); if (as->volume) afs_deactivate_volume(as->volume); @@ -689,7 +688,6 @@ static struct inode *afs_alloc_inode(struct super_block *sb) vnode->volume = NULL; vnode->lock_key = NULL; vnode->permit_cache = NULL; - RCU_INIT_POINTER(vnode->cb_interest, NULL); #ifdef CONFIG_AFS_FSCACHE vnode->cache = NULL; #endif @@ -719,8 +717,6 @@ static void afs_destroy_inode(struct inode *inode) _debug("DESTROY INODE %p", inode); - ASSERTCMP(rcu_access_pointer(vnode->cb_interest), ==, NULL); - atomic_dec(&afs_count_active_inodes); } diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c index c61dd9410202..093895c49c21 100644 --- a/fs/afs/vl_alias.c +++ b/fs/afs/vl_alias.c @@ -189,13 +189,13 @@ static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key, struct afs_volume *volume, *pvol = NULL; int ret; - /* Arbitrarily pick the first volume in the list. */ - read_lock(&p->proc_lock); - if (!list_empty(&p->proc_volumes)) - pvol = afs_get_volume(list_first_entry(&p->proc_volumes, - struct afs_volume, proc_link), + /* Arbitrarily pick a volume from the list. */ + read_seqlock_excl(&p->volume_lock); + if (!RB_EMPTY_ROOT(&p->volumes)) + pvol = afs_get_volume(rb_entry(p->volumes.rb_node, + struct afs_volume, cell_node), afs_volume_trace_get_query_alias); - read_unlock(&p->proc_lock); + read_sequnlock_excl(&p->volume_lock); if (!pvol) return 0; @@ -242,7 +242,7 @@ static int afs_query_for_alias(struct afs_cell *cell, struct key *key) hlist_for_each_entry(p, &cell->net->proc_cells, proc_link) { if (p == cell || p->alias_of) continue; - if (list_empty(&p->proc_volumes)) + if (RB_EMPTY_ROOT(&p->volumes)) continue; if (p->root_volume) continue; /* Ignore cells that have a root.cell volume. */ diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 0393f4910a92..9bc0509e3634 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -12,6 +12,56 @@ unsigned __read_mostly afs_volume_gc_delay = 10; unsigned __read_mostly afs_volume_record_life = 60 * 60; +/* + * Insert a volume into a cell. If there's an existing volume record, that is + * returned instead with a ref held. + */ +static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell, + struct afs_volume *volume) +{ + struct afs_volume *p; + struct rb_node *parent = NULL, **pp; + + write_seqlock(&cell->volume_lock); + + pp = &cell->volumes.rb_node; + while (*pp) { + parent = *pp; + p = rb_entry(parent, struct afs_volume, cell_node); + if (p->vid < volume->vid) { + pp = &(*pp)->rb_left; + } else if (p->vid > volume->vid) { + pp = &(*pp)->rb_right; + } else { + volume = afs_get_volume(p, afs_volume_trace_get_cell_insert); + goto found; + } + } + + rb_link_node_rcu(&volume->cell_node, parent, pp); + rb_insert_color(&volume->cell_node, &cell->volumes); + hlist_add_head_rcu(&volume->proc_link, &cell->proc_volumes); + +found: + write_sequnlock(&cell->volume_lock); + return volume; + +} + +static void afs_remove_volume_from_cell(struct afs_volume *volume) +{ + struct afs_cell *cell = volume->cell; + + if (!hlist_unhashed(&volume->proc_link)) { + trace_afs_volume(volume->vid, atomic_read(&volume->usage), + afs_volume_trace_remove); + write_seqlock(&cell->volume_lock); + hlist_del_rcu(&volume->proc_link); + rb_erase(&volume->cell_node, &cell->volumes); + write_sequnlock(&cell->volume_lock); + } +} + /* * Allocate a volume record and load it up from a vldb record. */ @@ -39,7 +89,7 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params, volume->name_len = vldb->name_len; atomic_set(&volume->usage, 1); - INIT_LIST_HEAD(&volume->proc_link); + INIT_HLIST_NODE(&volume->proc_link); rwlock_init(&volume->servers_lock); rwlock_init(&volume->cb_v_break_lock); memcpy(volume->name, vldb->name, vldb->name_len + 1); @@ -62,6 +112,25 @@ error_0: return ERR_PTR(ret); } +/* + * Look up or allocate a volume record. + */ +static struct afs_volume *afs_lookup_volume(struct afs_fs_context *params, + struct afs_vldb_entry *vldb, + unsigned long type_mask) +{ + struct afs_volume *candidate, *volume; + + candidate = afs_alloc_volume(params, vldb, type_mask); + if (IS_ERR(candidate)) + return candidate; + + volume = afs_insert_volume_into_cell(params->cell, candidate); + if (volume != candidate) + afs_put_volume(params->net, candidate, afs_volume_trace_put_cell_dup); + return volume; +} + /* * Look up a VLDB record for a volume. */ @@ -139,7 +208,7 @@ struct afs_volume *afs_create_volume(struct afs_fs_context *params) } type_mask = 1UL << params->type; - volume = afs_alloc_volume(params, vldb, type_mask); + volume = afs_lookup_volume(params, vldb, type_mask); error: kfree(vldb); @@ -157,11 +226,12 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume) ASSERTCMP(volume->cache, ==, NULL); #endif + afs_remove_volume_from_cell(volume); afs_put_serverlist(net, rcu_access_pointer(volume->servers)); afs_put_cell(net, volume->cell); trace_afs_volume(volume->vid, atomic_read(&volume->usage), afs_volume_trace_free); - kfree(volume); + kfree_rcu(volume, rcu); _leave(" [destroyed]"); } @@ -207,10 +277,6 @@ void afs_activate_volume(struct afs_volume *volume) NULL, 0, volume, 0, true); #endif - - write_lock(&volume->cell->proc_lock); - list_add_tail(&volume->proc_link, &volume->cell->proc_volumes); - write_unlock(&volume->cell->proc_lock); } /* @@ -220,10 +286,6 @@ void afs_deactivate_volume(struct afs_volume *volume) { _enter("%s", volume->name); - write_lock(&volume->cell->proc_lock); - list_del_init(&volume->proc_link); - write_unlock(&volume->cell->proc_lock); - #ifdef CONFIG_AFS_FSCACHE fscache_relinquish_cookie(volume->cache, NULL, test_bit(AFS_VOLUME_DELETED, &volume->flags)); diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index d0cd112a3720..b0a6e40b4da3 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -854,7 +854,7 @@ void yfs_fs_remove_file(struct afs_operation *op) _enter(""); - if (!test_bit(AFS_SERVER_FL_NO_RM2, &op->cbi->server->flags)) + if (!test_bit(AFS_SERVER_FL_NO_RM2, &op->server->flags)) return yfs_fs_remove_file2(op); call = afs_alloc_flat_call(op->net, &yfs_RXYFSRemoveFile, -- cgit v1.2.3-70-g09d2 From 3c4c4075fc61f5c37a0112b1dc8398025dc3e26a Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 27 May 2020 15:51:30 +0100 Subject: afs: Fix the by-UUID server tree to allow servers with the same UUID Whilst it shouldn't happen, it is possible for multiple fileservers to share a UUID, particularly if an entire cell has been duplicated, UUIDs and all. In such a case, it's not necessarily possible to map the effect of the CB.InitCallBackState3 incoming RPC to a specific server unambiguously by UUID and thus to a specific cell. Indeed, there's a problem whereby multiple server records may need to occupy the same spot in the rb_tree rooted in the afs_net struct. Fix this by allowing servers to form a list, with the head of the list in the tree. When the front entry in the list is removed, the second in the list just replaces it. afs_init_callback_state() then just goes down the line, poking each server in the list. This means that some servers will be unnecessarily poked, unfortunately. An alternative would be to route by call parameters. Reported-by: Jeffrey Altman Signed-off-by: David Howells Fixes: d2ddc776a458 ("afs: Overhaul volume and server record caching and fileserver rotation") --- fs/afs/callback.c | 10 ++++++++-- fs/afs/internal.h | 4 +++- fs/afs/server.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 59 insertions(+), 11 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/callback.c b/fs/afs/callback.c index b4cb9bb63f0a..7d9b23d981bf 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -21,11 +21,17 @@ #include "internal.h" /* - * allow the fileserver to request callback state (re-)initialisation + * Allow the fileserver to request callback state (re-)initialisation. + * Unfortunately, UUIDs are not guaranteed unique. */ void afs_init_callback_state(struct afs_server *server) { - server->cb_s_break++; + rcu_read_lock(); + do { + server->cb_s_break++; + server = rcu_dereference(server->uuid_next); + } while (0); + rcu_read_unlock(); } /* diff --git a/fs/afs/internal.h b/fs/afs/internal.h index c64c2b47ece7..e0dc14d4d8b9 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -486,7 +486,9 @@ struct afs_server { struct afs_addr_list __rcu *addresses; struct afs_cell *cell; /* Cell to which belongs (pins ref) */ - struct rb_node uuid_rb; /* Link in cell->fs_servers */ + struct rb_node uuid_rb; /* Link in net->fs_servers */ + struct afs_server __rcu *uuid_next; /* Next server with same UUID */ + struct afs_server *uuid_prev; /* Previous server with same UUID */ struct list_head probe_link; /* Link in net->fs_probe_list */ struct hlist_node addr4_link; /* Link in net->fs_addresses4 */ struct hlist_node addr6_link; /* Link in net->fs_addresses6 */ diff --git a/fs/afs/server.c b/fs/afs/server.c index c51039a077cd..88593ffcb54e 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -130,13 +130,15 @@ struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uu } /* - * Install a server record in the namespace tree + * Install a server record in the namespace tree. If there's a clash, we stick + * it into a list anchored on whichever afs_server struct is actually in the + * tree. */ static struct afs_server *afs_install_server(struct afs_cell *cell, struct afs_server *candidate) { const struct afs_addr_list *alist; - struct afs_server *server; + struct afs_server *server, *next; struct afs_net *net = cell->net; struct rb_node **pp, *p; int diff; @@ -153,12 +155,30 @@ static struct afs_server *afs_install_server(struct afs_cell *cell, _debug("- consider %p", p); server = rb_entry(p, struct afs_server, uuid_rb); diff = memcmp(&candidate->uuid, &server->uuid, sizeof(uuid_t)); - if (diff < 0) + if (diff < 0) { pp = &(*pp)->rb_left; - else if (diff > 0) + } else if (diff > 0) { pp = &(*pp)->rb_right; - else - goto exists; + } else { + if (server->cell == cell) + goto exists; + + /* We have the same UUID representing servers in + * different cells. Append the new server to the list. + */ + for (;;) { + next = rcu_dereference_protected( + server->uuid_next, + lockdep_is_held(&net->fs_lock.lock)); + if (!next) + break; + server = next; + } + rcu_assign_pointer(server->uuid_next, candidate); + candidate->uuid_prev = server; + server = candidate; + goto added_dup; + } } server = candidate; @@ -166,6 +186,7 @@ static struct afs_server *afs_install_server(struct afs_cell *cell, rb_insert_color(&server->uuid_rb, &net->fs_servers); hlist_add_head_rcu(&server->proc_link, &net->fs_proc); +added_dup: write_seqlock(&net->fs_addr_lock); alist = rcu_dereference_protected(server->addresses, lockdep_is_held(&net->fs_addr_lock.lock)); @@ -453,7 +474,7 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server) */ static void afs_gc_servers(struct afs_net *net, struct afs_server *gc_list) { - struct afs_server *server; + struct afs_server *server, *next, *prev; int active; while ((server = gc_list)) { @@ -465,7 +486,26 @@ static void afs_gc_servers(struct afs_net *net, struct afs_server *gc_list) if (active == 0) { trace_afs_server(server, atomic_read(&server->ref), active, afs_server_trace_gc); - rb_erase(&server->uuid_rb, &net->fs_servers); + next = rcu_dereference_protected( + server->uuid_next, lockdep_is_held(&net->fs_lock.lock)); + prev = server->uuid_prev; + if (!prev) { + /* The one at the front is in the tree */ + if (!next) { + rb_erase(&server->uuid_rb, &net->fs_servers); + } else { + rb_replace_node_rcu(&server->uuid_rb, + &next->uuid_rb, + &net->fs_servers); + next->uuid_prev = NULL; + } + } else { + /* This server is not at the front */ + rcu_assign_pointer(prev->uuid_next, next); + if (next) + next->uuid_prev = prev; + } + list_del(&server->probe_link); hlist_del_rcu(&server->proc_link); if (!hlist_unhashed(&server->addr4_link)) -- cgit v1.2.3-70-g09d2 From f11a016a852f32e9c991baf6a036390eac5b4266 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 1 May 2020 22:06:02 +0100 Subject: afs: Fix afs_statfs() to not let the values go below zero Fix afs_statfs() so that the value for f_bavail and f_bfree don't go "negative" if the number of blocks in use by a volume exceeds the max quota for that volume. Signed-off-by: David Howells --- fs/afs/super.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs/afs') diff --git a/fs/afs/super.c b/fs/afs/super.c index c77b11b31233..b552357b1d13 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -729,7 +729,10 @@ static void afs_get_volume_status_success(struct afs_operation *op) buf->f_blocks = vs->part_max_blocks; else buf->f_blocks = vs->max_quota; - buf->f_bavail = buf->f_bfree = buf->f_blocks - vs->blocks_in_use; + + if (buf->f_blocks > vs->blocks_in_use) + buf->f_bavail = buf->f_bfree = + buf->f_blocks - vs->blocks_in_use; } static const struct afs_operation_ops afs_get_volume_status_operation = { -- cgit v1.2.3-70-g09d2 From f3c130e6e6d15822e1553531f91ecc8f3375bac3 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sat, 2 May 2020 13:39:57 +0100 Subject: afs: Don't use probe running state to make decisions outside probe code Don't use the running state for fileserver probes to make decisions about which server to use as the state is cleared at the start of a probe and also intermediate values might be misleading. Instead, add a separate 'latest known' rtt in the afs_server struct and a flag to indicate if the server is known to be responding and update these as and when we know what to change them to. Signed-off-by: David Howells --- fs/afs/fs_probe.c | 18 ++++++++++++------ fs/afs/internal.h | 4 +++- fs/afs/rotate.c | 3 ++- fs/afs/server.c | 1 + 4 files changed, 18 insertions(+), 8 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/fs_probe.c b/fs/afs/fs_probe.c index 442b5e7944ff..c41cf3b2ab89 100644 --- a/fs/afs/fs_probe.c +++ b/fs/afs/fs_probe.c @@ -42,10 +42,13 @@ static void afs_finished_fs_probe(struct afs_net *net, struct afs_server *server bool responded = server->probe.responded; write_seqlock(&net->fs_lock); - if (responded) + if (responded) { list_add_tail(&server->probe_link, &net->fs_probe_slow); - else + } else { + server->rtt = UINT_MAX; + clear_bit(AFS_SERVER_FL_RESPONDING, &server->flags); list_add_tail(&server->probe_link, &net->fs_probe_fast); + } write_sequnlock(&net->fs_lock); afs_schedule_fs_probe(net, server, !responded); @@ -161,12 +164,14 @@ responded: rtt_us = rxrpc_kernel_get_srtt(call->net->socket, call->rxcall); if (rtt_us < server->probe.rtt) { server->probe.rtt = rtt_us; + server->rtt = rtt_us; alist->preferred = index; } smp_wmb(); /* Set rtt before responded. */ server->probe.responded = true; set_bit(index, &alist->responded); + set_bit(AFS_SERVER_FL_RESPONDING, &server->flags); out: spin_unlock(&server->probe_lock); @@ -224,7 +229,7 @@ int afs_wait_for_fs_probes(struct afs_server_list *slist, unsigned long untried) { struct wait_queue_entry *waits; struct afs_server *server; - unsigned int rtt = UINT_MAX; + unsigned int rtt = UINT_MAX, rtt_s; bool have_responders = false; int pref = -1, i; @@ -280,10 +285,11 @@ stop: for (i = 0; i < slist->nr_servers; i++) { if (test_bit(i, &untried)) { server = slist->servers[i].server; - if (server->probe.responded && - server->probe.rtt < rtt) { + rtt_s = READ_ONCE(server->rtt); + if (test_bit(AFS_SERVER_FL_RESPONDING, &server->flags) && + rtt_s < rtt) { pref = i; - rtt = server->probe.rtt; + rtt = rtt_s; } remove_wait_queue(&server->probe_wq, &waits[i]); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index e0dc14d4d8b9..a4fe5d1a8b53 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -496,6 +496,7 @@ struct afs_server { struct afs_server *gc_next; /* Next server in manager's list */ time64_t unuse_time; /* Time at which last unused */ unsigned long flags; +#define AFS_SERVER_FL_RESPONDING 0 /* The server is responding */ #define AFS_SERVER_FL_NOT_READY 1 /* The record is not ready for use */ #define AFS_SERVER_FL_NOT_FOUND 2 /* VL server says no such server */ #define AFS_SERVER_FL_VL_FAIL 3 /* Failed to access VL server */ @@ -508,6 +509,7 @@ struct afs_server { atomic_t ref; /* Object refcount */ atomic_t active; /* Active user count */ u32 addr_version; /* Address list version */ + unsigned int rtt; /* Server's current RTT in uS */ unsigned int debug_id; /* Debugging ID for traces */ /* file service access */ @@ -522,7 +524,7 @@ struct afs_server { atomic_t probe_outstanding; spinlock_t probe_lock; struct { - unsigned int rtt; /* RTT as ktime/64 */ + unsigned int rtt; /* RTT in uS */ u32 abort_code; short error; bool responded:1; diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index 8d5473cd8ea4..14863678ae9e 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -341,7 +341,8 @@ pick_server: for (i = 0; i < op->server_list->nr_servers; i++) { struct afs_server *s = op->server_list->servers[i].server; - if (!test_bit(i, &op->untried) || !s->probe.responded) + if (!test_bit(i, &op->untried) || + !test_bit(AFS_SERVER_FL_RESPONDING, &s->flags)) continue; if (s->probe.rtt < rtt) { op->index = i; diff --git a/fs/afs/server.c b/fs/afs/server.c index 88593ffcb54e..039e3488511c 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -239,6 +239,7 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell, INIT_LIST_HEAD(&server->probe_link); spin_lock_init(&server->probe_lock); server->cell = cell; + server->rtt = UINT_MAX; afs_inc_servers_outstanding(net); trace_afs_server(server, 1, 1, afs_server_trace_alloc); -- cgit v1.2.3-70-g09d2 From 32275d3f758f1252511709b77b3bab060a0e1d4f Mon Sep 17 00:00:00 2001 From: David Howells Date: Sat, 2 May 2020 13:44:50 +0100 Subject: afs: Show more a bit more server state in /proc/net/afs/servers Display more information about the state of a server record, including the flags, rtt and break counter plus the probe state for each server in /proc/net/afs/servers. Rearrange the server flags a bit to make them easier to read at a glance in the proc file. Signed-off-by: David Howells --- fs/afs/internal.h | 16 ++++++++-------- fs/afs/proc.c | 10 +++++++--- 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/internal.h b/fs/afs/internal.h index a4fe5d1a8b53..af0b7fca87db 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -497,15 +497,15 @@ struct afs_server { time64_t unuse_time; /* Time at which last unused */ unsigned long flags; #define AFS_SERVER_FL_RESPONDING 0 /* The server is responding */ -#define AFS_SERVER_FL_NOT_READY 1 /* The record is not ready for use */ -#define AFS_SERVER_FL_NOT_FOUND 2 /* VL server says no such server */ -#define AFS_SERVER_FL_VL_FAIL 3 /* Failed to access VL server */ -#define AFS_SERVER_FL_UPDATING 4 -#define AFS_SERVER_FL_NO_IBULK 7 /* Fileserver doesn't support FS.InlineBulkStatus */ +#define AFS_SERVER_FL_UPDATING 1 +#define AFS_SERVER_FL_NEEDS_UPDATE 2 /* Fileserver address list is out of date */ +#define AFS_SERVER_FL_NOT_READY 4 /* The record is not ready for use */ +#define AFS_SERVER_FL_NOT_FOUND 5 /* VL server says no such server */ +#define AFS_SERVER_FL_VL_FAIL 6 /* Failed to access VL server */ #define AFS_SERVER_FL_MAY_HAVE_CB 8 /* May have callbacks on this fileserver */ -#define AFS_SERVER_FL_IS_YFS 9 /* Server is YFS not AFS */ -#define AFS_SERVER_FL_NO_RM2 10 /* Fileserver doesn't support YFS.RemoveFile2 */ -#define AFS_SERVER_FL_NEEDS_UPDATE 12 /* Fileserver address list is out of date */ +#define AFS_SERVER_FL_IS_YFS 16 /* Server is YFS not AFS */ +#define AFS_SERVER_FL_NO_IBULK 17 /* Fileserver doesn't support FS.InlineBulkStatus */ +#define AFS_SERVER_FL_NO_RM2 18 /* Fileserver doesn't support YFS.RemoveFile2 */ atomic_t ref; /* Object refcount */ atomic_t active; /* Active user count */ u32 addr_version; /* Address list version */ diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 309a7b578255..22d00cf1913d 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -386,9 +386,13 @@ static int afs_proc_servers_show(struct seq_file *m, void *v) &server->uuid, atomic_read(&server->ref), atomic_read(&server->active)); - seq_printf(m, " - ALIST v=%u osp=%u r=%lx f=%lx\n", - alist->version, atomic_read(&server->probe_outstanding), - alist->responded, alist->failed); + seq_printf(m, " - info: fl=%lx rtt=%u brk=%x\n", + server->flags, server->rtt, server->cb_s_break); + seq_printf(m, " - probe: last=%d out=%d\n", + (int)(jiffies - server->probed_at) / HZ, + atomic_read(&server->probe_outstanding)); + seq_printf(m, " - ALIST v=%u rsp=%lx f=%lx\n", + alist->version, alist->responded, alist->failed); for (i = 0; i < alist->nr_addrs; i++) seq_printf(m, " [%x] %pISpc%s\n", i, &alist->addrs[i].transport, -- cgit v1.2.3-70-g09d2 From 8409f67b6437c4b327ee95a71081b9c7bfee0b00 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 22 Apr 2020 00:02:46 +0100 Subject: afs: Adjust the fileserver rotation algorithm to reprobe/retry more quickly Adjust the fileserver rotation algorithm so that if we've tried all the addresses on a server (cumulatively over multiple operations) until we've run out of untried addresses, immediately reprobe all that server's interfaces and retry the op at least once before we move onto the next server. Signed-off-by: David Howells --- fs/afs/fs_probe.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ fs/afs/internal.h | 24 ++++++++++++++---------- fs/afs/rotate.c | 29 +++++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 12 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/fs_probe.c b/fs/afs/fs_probe.c index c41cf3b2ab89..b34f74b0f319 100644 --- a/fs/afs/fs_probe.c +++ b/fs/afs/fs_probe.c @@ -338,6 +338,18 @@ static void afs_dispatch_fs_probe(struct afs_net *net, struct afs_server *server afs_put_server(net, server, afs_server_trace_put_probe); } +/* + * Probe a server immediately without waiting for its due time to come + * round. This is used when all of the addresses have been tried. + */ +void afs_probe_fileserver(struct afs_net *net, struct afs_server *server) +{ + write_seqlock(&net->fs_lock); + if (!list_empty(&server->probe_link)) + return afs_dispatch_fs_probe(net, server, true); + write_sequnlock(&net->fs_lock); +} + /* * Probe dispatcher to regularly dispatch probes to keep NAT alive. */ @@ -411,3 +423,38 @@ again: _leave(" [quiesce]"); } } + +/* + * Wait for a probe on a particular fileserver to complete for 2s. + */ +int afs_wait_for_one_fs_probe(struct afs_server *server, bool is_intr) +{ + struct wait_queue_entry wait; + unsigned long timo = 2 * HZ; + + if (atomic_read(&server->probe_outstanding) == 0) + goto dont_wait; + + init_wait_entry(&wait, 0); + for (;;) { + prepare_to_wait_event(&server->probe_wq, &wait, + is_intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + if (timo == 0 || + server->probe.responded || + atomic_read(&server->probe_outstanding) == 0 || + (is_intr && signal_pending(current))) + break; + timo = schedule_timeout(timo); + } + + finish_wait(&server->probe_wq, &wait); + +dont_wait: + if (server->probe.responded) + return 0; + if (is_intr && signal_pending(current)) + return -ERESTARTSYS; + if (timo == 0) + return -ETIME; + return -EDESTADDRREQ; +} diff --git a/fs/afs/internal.h b/fs/afs/internal.h index af0b7fca87db..e1621b0670cc 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -826,16 +826,18 @@ struct afs_operation { unsigned short nr_iterations; /* Number of server iterations */ unsigned int flags; -#define AFS_OPERATION_STOP 0x0001 /* Set to cease iteration */ -#define AFS_OPERATION_VBUSY 0x0002 /* Set if seen VBUSY */ -#define AFS_OPERATION_VMOVED 0x0004 /* Set if seen VMOVED */ -#define AFS_OPERATION_VNOVOL 0x0008 /* Set if seen VNOVOL */ -#define AFS_OPERATION_CUR_ONLY 0x0010 /* Set if current server only (file lock held) */ -#define AFS_OPERATION_NO_VSLEEP 0x0020 /* Set to prevent sleep on VBUSY, VOFFLINE, ... */ -#define AFS_OPERATION_UNINTR 0x0040 /* Set if op is uninterruptible */ -#define AFS_OPERATION_DOWNGRADE 0x0080 /* Set to retry with downgraded opcode */ -#define AFS_OPERATION_LOCK_0 0x0100 /* Set if have io_lock on file[0] */ -#define AFS_OPERATION_LOCK_1 0x0200 /* Set if have io_lock on file[1] */ +#define AFS_OPERATION_STOP 0x0001 /* Set to cease iteration */ +#define AFS_OPERATION_VBUSY 0x0002 /* Set if seen VBUSY */ +#define AFS_OPERATION_VMOVED 0x0004 /* Set if seen VMOVED */ +#define AFS_OPERATION_VNOVOL 0x0008 /* Set if seen VNOVOL */ +#define AFS_OPERATION_CUR_ONLY 0x0010 /* Set if current server only (file lock held) */ +#define AFS_OPERATION_NO_VSLEEP 0x0020 /* Set to prevent sleep on VBUSY, VOFFLINE, ... */ +#define AFS_OPERATION_UNINTR 0x0040 /* Set if op is uninterruptible */ +#define AFS_OPERATION_DOWNGRADE 0x0080 /* Set to retry with downgraded opcode */ +#define AFS_OPERATION_LOCK_0 0x0100 /* Set if have io_lock on file[0] */ +#define AFS_OPERATION_LOCK_1 0x0200 /* Set if have io_lock on file[1] */ +#define AFS_OPERATION_TRIED_ALL 0x0400 /* Set if we've tried all the fileservers */ +#define AFS_OPERATION_RETRY_SERVER 0x0800 /* Set if we should retry the current server */ }; /* @@ -1055,7 +1057,9 @@ static inline void afs_op_set_fid(struct afs_operation *op, unsigned int n, extern void afs_fileserver_probe_result(struct afs_call *); extern void afs_fs_probe_fileserver(struct afs_net *, struct afs_server *, struct key *, bool); extern int afs_wait_for_fs_probes(struct afs_server_list *, unsigned long); +extern void afs_probe_fileserver(struct afs_net *, struct afs_server *); extern void afs_fs_probe_dispatcher(struct work_struct *); +extern int afs_wait_for_one_fs_probe(struct afs_server *, bool); /* * inode.c diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index 14863678ae9e..6a0935cb822f 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -369,6 +369,7 @@ selected_server: _debug("USING SERVER: %pU", &server->uuid); + op->flags |= AFS_OPERATION_RETRY_SERVER; op->server = server; if (vnode->cb_server != server) { vnode->cb_server = server; @@ -383,6 +384,7 @@ selected_server: afs_get_addrlist(alist); read_unlock(&server->fs_lock); +retry_server: memset(&op->ac, 0, sizeof(op->ac)); if (!op->ac.alist) @@ -398,13 +400,36 @@ iterate_address: * address on which it will respond to us. */ if (!afs_iterate_addresses(&op->ac)) - goto next_server; + goto out_of_addresses; - _debug("address [%u] %u/%u", op->index, op->ac.index, op->ac.alist->nr_addrs); + _debug("address [%u] %u/%u %pISp", + op->index, op->ac.index, op->ac.alist->nr_addrs, + &op->ac.alist->addrs[op->ac.index].transport); _leave(" = t"); return true; +out_of_addresses: + /* We've now had a failure to respond on all of a server's addresses - + * immediately probe them again and consider retrying the server. + */ + afs_probe_fileserver(op->net, op->server); + if (op->flags & AFS_OPERATION_RETRY_SERVER) { + alist = op->ac.alist; + error = afs_wait_for_one_fs_probe( + op->server, !(op->flags & AFS_OPERATION_UNINTR)); + switch (error) { + case 0: + op->flags &= ~AFS_OPERATION_RETRY_SERVER; + goto retry_server; + case -ERESTARTSYS: + goto failed_set_error; + case -ETIME: + case -EDESTADDRREQ: + goto next_server; + } + } + next_server: _debug("next"); afs_end_cursor(&op->ac); -- cgit v1.2.3-70-g09d2