From 445f9b6952869586990ec3140dcd87c86d795d2e Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Nov 2023 13:01:11 +0000 Subject: 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: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- fs/afs/cell.c | 2 +- fs/afs/fs_operation.c | 2 +- fs/afs/internal.h | 3 ++- fs/afs/super.c | 7 +++---- fs/afs/vl_alias.c | 6 +++--- fs/afs/volume.c | 15 +++++++++------ 6 files changed, 19 insertions(+), 16 deletions(-) (limited to 'fs/afs') diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 7c0dce8eecad..55ee194e31ff 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -818,7 +818,7 @@ done: final_destruction: /* 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; afs_put_cell(cell, afs_cell_trace_put_destroy); } diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c index 8c6d827f999d..10137681aa7d 100644 --- a/fs/afs/fs_operation.c +++ b/fs/afs/fs_operation.c @@ -265,7 +265,7 @@ int afs_put_operation(struct afs_operation *op) } 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); kfree(op); return ret; diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 0b726bd2cf8c..a50dfb2f8d7d 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -636,6 +636,7 @@ struct afs_volume { 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 */ + struct work_struct destructor; /* Deferred destructor */ 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 */ @@ -1613,7 +1614,7 @@ extern int afs_activate_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); 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 *); /* diff --git a/fs/afs/super.c b/fs/afs/super.c index a01a0fb2cdbb..ae2d66a52add 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -381,8 +381,7 @@ static int afs_validate_fc(struct fs_context *fc) ctx->key = key; if (ctx->volume) { - afs_put_volume(ctx->net, ctx->volume, - afs_volume_trace_put_validate_fc); + afs_put_volume(ctx->volume, afs_volume_trace_put_validate_fc); ctx->volume = NULL; } @@ -529,7 +528,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_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); put_net(as->net_ns); kfree(as); @@ -615,7 +614,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_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); key_put(ctx->key); kfree(ctx); diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c index 63e7ed324af9..9f36e14f1c2d 100644 --- a/fs/afs/vl_alias.c +++ b/fs/afs/vl_alias.c @@ -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. */ volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len); 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) return PTR_ERR(volume); /* 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, rcu_read_unlock(); } - afs_put_volume(cell->net, volume, afs_volume_trace_put_query_alias); - afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias); + afs_put_volume(volume, afs_volume_trace_put_query_alias); + afs_put_volume(pvol, afs_volume_trace_put_query_alias); return ret; } diff --git a/fs/afs/volume.c b/fs/afs/volume.c index aefb982dee9a..4982fce25057 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -11,6 +11,8 @@ 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 * returned instead with a ref held. @@ -91,6 +93,7 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params, refcount_set(&volume->ref, 1); INIT_HLIST_NODE(&volume->proc_link); + INIT_WORK(&volume->destructor, afs_destroy_volume); rwlock_init(&volume->servers_lock); rwlock_init(&volume->cb_v_break_lock); 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, if (volume == candidate) afs_attach_volume_to_servers(volume, slist); 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; } @@ -223,8 +226,9 @@ error: /* * 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); _enter("%p", 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_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); trace_afs_volume(volume->vid, refcount_read(&volume->ref), afs_volume_trace_free); @@ -277,8 +281,7 @@ struct afs_volume *afs_get_volume(struct afs_volume *volume, /* * Drop a reference on a volume record. */ -void afs_put_volume(struct afs_net *net, struct afs_volume *volume, - enum afs_volume_trace reason) +void afs_put_volume(struct afs_volume *volume, enum afs_volume_trace reason) { if (volume) { afs_volid_t vid = volume->vid; @@ -288,7 +291,7 @@ void afs_put_volume(struct afs_net *net, struct afs_volume *volume, zero = __refcount_dec_and_test(&volume->ref, &r); trace_afs_volume(vid, r - 1, reason); if (zero) - afs_destroy_volume(net, volume); + schedule_work(&volume->destructor); } } -- cgit