Commit 445f9b69 authored by David Howells's avatar David Howells

afs: Defer volume record destruction to a workqueue

Defer volume record destruction to a workqueue so that afs_put_volume()
isn't going to run the destruction process in the callback workqueue whilst
the server is holding up other clients whilst waiting for us to reply to a
CB.CallBack notification RPC.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
parent ca0e79a4
...@@ -818,7 +818,7 @@ static void afs_manage_cell(struct afs_cell *cell) ...@@ -818,7 +818,7 @@ static void afs_manage_cell(struct afs_cell *cell)
final_destruction: final_destruction:
/* The root volume is pinning the cell */ /* The root volume is pinning the cell */
afs_put_volume(cell->net, cell->root_volume, afs_volume_trace_put_cell_root); afs_put_volume(cell->root_volume, afs_volume_trace_put_cell_root);
cell->root_volume = NULL; cell->root_volume = NULL;
afs_put_cell(cell, afs_cell_trace_put_destroy); afs_put_cell(cell, afs_cell_trace_put_destroy);
} }
......
...@@ -265,7 +265,7 @@ int afs_put_operation(struct afs_operation *op) ...@@ -265,7 +265,7 @@ int afs_put_operation(struct afs_operation *op)
} }
afs_put_serverlist(op->net, op->server_list); afs_put_serverlist(op->net, op->server_list);
afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op); afs_put_volume(op->volume, afs_volume_trace_put_put_op);
key_put(op->key); key_put(op->key);
kfree(op); kfree(op);
return ret; return ret;
......
...@@ -636,6 +636,7 @@ struct afs_volume { ...@@ -636,6 +636,7 @@ struct afs_volume {
struct rb_node cell_node; /* Link in cell->volumes */ struct rb_node cell_node; /* Link in cell->volumes */
struct hlist_node proc_link; /* Link in cell->proc_volumes */ struct hlist_node proc_link; /* Link in cell->proc_volumes */
struct super_block __rcu *sb; /* Superblock on which inodes reside */ struct super_block __rcu *sb; /* Superblock on which inodes reside */
struct work_struct destructor; /* Deferred destructor */
unsigned long flags; unsigned long flags;
#define AFS_VOLUME_NEEDS_UPDATE 0 /* - T if an update needs performing */ #define AFS_VOLUME_NEEDS_UPDATE 0 /* - T if an update needs performing */
#define AFS_VOLUME_UPDATING 1 /* - T if an update is in progress */ #define AFS_VOLUME_UPDATING 1 /* - T if an update is in progress */
...@@ -1613,7 +1614,7 @@ extern int afs_activate_volume(struct afs_volume *); ...@@ -1613,7 +1614,7 @@ extern int afs_activate_volume(struct afs_volume *);
extern void afs_deactivate_volume(struct afs_volume *); extern void afs_deactivate_volume(struct afs_volume *);
bool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason); bool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason);
extern struct afs_volume *afs_get_volume(struct afs_volume *, enum afs_volume_trace); 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); void afs_put_volume(struct afs_volume *volume, enum afs_volume_trace reason);
extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *); extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *);
/* /*
......
...@@ -381,8 +381,7 @@ static int afs_validate_fc(struct fs_context *fc) ...@@ -381,8 +381,7 @@ static int afs_validate_fc(struct fs_context *fc)
ctx->key = key; ctx->key = key;
if (ctx->volume) { if (ctx->volume) {
afs_put_volume(ctx->net, ctx->volume, afs_put_volume(ctx->volume, afs_volume_trace_put_validate_fc);
afs_volume_trace_put_validate_fc);
ctx->volume = NULL; ctx->volume = NULL;
} }
...@@ -529,7 +528,7 @@ static void afs_destroy_sbi(struct afs_super_info *as) ...@@ -529,7 +528,7 @@ static void afs_destroy_sbi(struct afs_super_info *as)
{ {
if (as) { if (as) {
struct afs_net *net = afs_net(as->net_ns); struct afs_net *net = afs_net(as->net_ns);
afs_put_volume(net, as->volume, afs_volume_trace_put_destroy_sbi); afs_put_volume(as->volume, afs_volume_trace_put_destroy_sbi);
afs_unuse_cell(net, as->cell, afs_cell_trace_unuse_sbi); afs_unuse_cell(net, as->cell, afs_cell_trace_unuse_sbi);
put_net(as->net_ns); put_net(as->net_ns);
kfree(as); kfree(as);
...@@ -615,7 +614,7 @@ static void afs_free_fc(struct fs_context *fc) ...@@ -615,7 +614,7 @@ static void afs_free_fc(struct fs_context *fc)
struct afs_fs_context *ctx = fc->fs_private; struct afs_fs_context *ctx = fc->fs_private;
afs_destroy_sbi(fc->s_fs_info); afs_destroy_sbi(fc->s_fs_info);
afs_put_volume(ctx->net, ctx->volume, afs_volume_trace_put_free_fc); afs_put_volume(ctx->volume, afs_volume_trace_put_free_fc);
afs_unuse_cell(ctx->net, ctx->cell, afs_cell_trace_unuse_fc); afs_unuse_cell(ctx->net, ctx->cell, afs_cell_trace_unuse_fc);
key_put(ctx->key); key_put(ctx->key);
kfree(ctx); kfree(ctx);
......
...@@ -156,7 +156,7 @@ static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key, ...@@ -156,7 +156,7 @@ static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key,
/* And see if it's in the new cell. */ /* And see if it's in the new cell. */
volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len); volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len);
if (IS_ERR(volume)) { if (IS_ERR(volume)) {
afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias); afs_put_volume(pvol, afs_volume_trace_put_query_alias);
if (PTR_ERR(volume) != -ENOMEDIUM) if (PTR_ERR(volume) != -ENOMEDIUM)
return PTR_ERR(volume); return PTR_ERR(volume);
/* That volume is not in the new cell, so not an alias */ /* That volume is not in the new cell, so not an alias */
...@@ -174,8 +174,8 @@ static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key, ...@@ -174,8 +174,8 @@ static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key,
rcu_read_unlock(); rcu_read_unlock();
} }
afs_put_volume(cell->net, volume, afs_volume_trace_put_query_alias); afs_put_volume(volume, afs_volume_trace_put_query_alias);
afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias); afs_put_volume(pvol, afs_volume_trace_put_query_alias);
return ret; return ret;
} }
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
static unsigned __read_mostly afs_volume_record_life = 60 * 60; static unsigned __read_mostly afs_volume_record_life = 60 * 60;
static void afs_destroy_volume(struct work_struct *work);
/* /*
* Insert a volume into a cell. If there's an existing volume record, that is * Insert a volume into a cell. If there's an existing volume record, that is
* returned instead with a ref held. * returned instead with a ref held.
...@@ -91,6 +93,7 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params, ...@@ -91,6 +93,7 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
refcount_set(&volume->ref, 1); refcount_set(&volume->ref, 1);
INIT_HLIST_NODE(&volume->proc_link); INIT_HLIST_NODE(&volume->proc_link);
INIT_WORK(&volume->destructor, afs_destroy_volume);
rwlock_init(&volume->servers_lock); rwlock_init(&volume->servers_lock);
rwlock_init(&volume->cb_v_break_lock); rwlock_init(&volume->cb_v_break_lock);
memcpy(volume->name, vldb->name, vldb->name_len + 1); memcpy(volume->name, vldb->name, vldb->name_len + 1);
...@@ -133,7 +136,7 @@ static struct afs_volume *afs_lookup_volume(struct afs_fs_context *params, ...@@ -133,7 +136,7 @@ static struct afs_volume *afs_lookup_volume(struct afs_fs_context *params,
if (volume == candidate) if (volume == candidate)
afs_attach_volume_to_servers(volume, slist); afs_attach_volume_to_servers(volume, slist);
else else
afs_put_volume(params->net, candidate, afs_volume_trace_put_cell_dup); afs_put_volume(candidate, afs_volume_trace_put_cell_dup);
return volume; return volume;
} }
...@@ -223,8 +226,9 @@ struct afs_volume *afs_create_volume(struct afs_fs_context *params) ...@@ -223,8 +226,9 @@ struct afs_volume *afs_create_volume(struct afs_fs_context *params)
/* /*
* Destroy a volume record * Destroy a volume record
*/ */
static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume) static void afs_destroy_volume(struct work_struct *work)
{ {
struct afs_volume *volume = container_of(work, struct afs_volume, destructor);
struct afs_server_list *slist = rcu_access_pointer(volume->servers); struct afs_server_list *slist = rcu_access_pointer(volume->servers);
_enter("%p", volume); _enter("%p", volume);
...@@ -235,7 +239,7 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume) ...@@ -235,7 +239,7 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
afs_detach_volume_from_servers(volume, slist); afs_detach_volume_from_servers(volume, slist);
afs_remove_volume_from_cell(volume); afs_remove_volume_from_cell(volume);
afs_put_serverlist(net, slist); afs_put_serverlist(volume->cell->net, slist);
afs_put_cell(volume->cell, afs_cell_trace_put_vol); afs_put_cell(volume->cell, afs_cell_trace_put_vol);
trace_afs_volume(volume->vid, refcount_read(&volume->ref), trace_afs_volume(volume->vid, refcount_read(&volume->ref),
afs_volume_trace_free); afs_volume_trace_free);
...@@ -277,8 +281,7 @@ struct afs_volume *afs_get_volume(struct afs_volume *volume, ...@@ -277,8 +281,7 @@ struct afs_volume *afs_get_volume(struct afs_volume *volume,
/* /*
* Drop a reference on a volume record. * Drop a reference on a volume record.
*/ */
void afs_put_volume(struct afs_net *net, struct afs_volume *volume, void afs_put_volume(struct afs_volume *volume, enum afs_volume_trace reason)
enum afs_volume_trace reason)
{ {
if (volume) { if (volume) {
afs_volid_t vid = volume->vid; afs_volid_t vid = volume->vid;
...@@ -288,7 +291,7 @@ void afs_put_volume(struct afs_net *net, struct afs_volume *volume, ...@@ -288,7 +291,7 @@ void afs_put_volume(struct afs_net *net, struct afs_volume *volume,
zero = __refcount_dec_and_test(&volume->ref, &r); zero = __refcount_dec_and_test(&volume->ref, &r);
trace_afs_volume(vid, r - 1, reason); trace_afs_volume(vid, r - 1, reason);
if (zero) if (zero)
afs_destroy_volume(net, volume); schedule_work(&volume->destructor);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment