mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
afs: Simplify cell record handling
Simplify afs_cell record handling to avoid very occasional races that cause
module removal to hang (it waits for all cell records to be removed).
There are two things that particularly contribute to the difficulty:
firstly, the code tries to pass a ref on the cell to the cell's maintenance
work item (which gets awkward if the work item is already queued); and,
secondly, there's an overall cell manager that tries to use just one timer
for the entire cell collection (to avoid having loads of timers). However,
both of these are probably unnecessarily restrictive.
To simplify this, the following changes are made:
(1) The cell record collection manager is removed. Each cell record
manages itself individually.
(2) Each afs_cell is given a second work item (cell->destroyer) that is
queued when its refcount reaches zero. This is not done in the
context of the putting thread as it might be in an inconvenient place
to sleep.
(3) Each afs_cell is given its own timer. The timer is used to expire the
cell record after a period of unuse if not otherwise pinned and can
also be used for other maintenance tasks if necessary (of which there
are currently none as DNS refresh is triggered by filesystem
operations).
(4) The afs_cell manager work item (cell->manager) is no longer given a
ref on the cell when queued; rather, the manager must be deleted.
This does away with the need to deal with the consequences of losing a
race to queue cell->manager. Clean up of extra queuing is deferred to
the destroyer.
(5) The cell destroyer work item makes sure the cell timer is removed and
that the normal cell work is cancelled before farming the actual
destruction off to RCU.
(6) When a network namespace is destroyed or the kafs module is unloaded,
it's now a simple matter of marking the namespace as dead then just
waking up all the cell work items. They will then remove and destroy
themselves once all remaining activity counts and/or a ref counts are
dropped. This makes sure that all server records are dropped first.
(7) The cell record state set is reduced to just four states: SETTING_UP,
ACTIVE, REMOVING and DEAD. The record persists in the active state
even when it's not being used until the time comes to remove it rather
than downgrading it to an inactive state from whence it can be
restored.
This means that the cell still appears in /proc and /afs when not in
use until it switches to the REMOVING state - at which point it is
removed.
Note that the REMOVING state is included so that someone wanting to
resurrect the cell record is forced to wait whilst the cell is torn
down in that state. Once it's in the DEAD state, it has been removed
from net->cells tree and is no longer findable and can be replaced.
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
cc: linux-fsdevel@vger.kernel.org
Link: https://lore.kernel.org/r/20250224234154.2014840-16-dhowells@redhat.com/ # v1
Link: https://lore.kernel.org/r/20250310094206.801057-12-dhowells@redhat.com/ # v4
This commit is contained in:
+165
-243
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -293,8 +293,8 @@ static int afs_dynroot_readdir_cells(struct afs_net *net, struct dir_context *ct
|
||||
cell = idr_get_next(&net->cells_dyn_ino, &ix);
|
||||
if (!cell)
|
||||
return 0;
|
||||
if (READ_ONCE(cell->state) == AFS_CELL_FAILED ||
|
||||
READ_ONCE(cell->state) == AFS_CELL_REMOVED) {
|
||||
if (READ_ONCE(cell->state) == AFS_CELL_REMOVING ||
|
||||
READ_ONCE(cell->state) == AFS_CELL_DEAD) {
|
||||
ctx->pos += 2;
|
||||
ctx->pos &= ~1;
|
||||
continue;
|
||||
|
||||
+6
-10
@@ -289,8 +289,6 @@ struct afs_net {
|
||||
struct rb_root cells;
|
||||
struct idr cells_dyn_ino; /* cell->dynroot_ino mapping */
|
||||
struct afs_cell __rcu *ws_cell;
|
||||
struct work_struct cells_manager;
|
||||
struct timer_list cells_timer;
|
||||
atomic_t cells_outstanding;
|
||||
struct rw_semaphore cells_lock;
|
||||
struct mutex cells_alias_lock;
|
||||
@@ -339,13 +337,10 @@ struct afs_net {
|
||||
extern const char afs_init_sysname[];
|
||||
|
||||
enum afs_cell_state {
|
||||
AFS_CELL_UNSET,
|
||||
AFS_CELL_ACTIVATING,
|
||||
AFS_CELL_SETTING_UP,
|
||||
AFS_CELL_ACTIVE,
|
||||
AFS_CELL_DEACTIVATING,
|
||||
AFS_CELL_INACTIVE,
|
||||
AFS_CELL_FAILED,
|
||||
AFS_CELL_REMOVED,
|
||||
AFS_CELL_REMOVING,
|
||||
AFS_CELL_DEAD,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -376,7 +371,9 @@ struct afs_cell {
|
||||
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 destroyer; /* Destroyer for cell */
|
||||
struct work_struct manager; /* Manager for init/deinit/dns */
|
||||
struct timer_list management_timer; /* General management timer */
|
||||
struct hlist_node proc_link; /* /proc cell list link */
|
||||
time64_t dns_expiry; /* Time AFSDB/SRV record expires */
|
||||
time64_t last_inactive; /* Time of last drop of usage count */
|
||||
@@ -1053,8 +1050,7 @@ extern struct afs_cell *afs_get_cell(struct afs_cell *, enum afs_cell_trace);
|
||||
extern void afs_see_cell(struct afs_cell *, enum afs_cell_trace);
|
||||
extern void afs_put_cell(struct afs_cell *, enum afs_cell_trace);
|
||||
extern void afs_queue_cell(struct afs_cell *, enum afs_cell_trace);
|
||||
extern void afs_manage_cells(struct work_struct *);
|
||||
extern void afs_cells_timer(struct timer_list *);
|
||||
void afs_set_cell_timer(struct afs_cell *cell, unsigned int delay_secs);
|
||||
extern void __net_exit afs_cell_purge(struct afs_net *);
|
||||
|
||||
/*
|
||||
|
||||
@@ -78,9 +78,6 @@ static int __net_init afs_net_init(struct net *net_ns)
|
||||
net->cells = RB_ROOT;
|
||||
idr_init(&net->cells_dyn_ino);
|
||||
init_rwsem(&net->cells_lock);
|
||||
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);
|
||||
|
||||
+4
-4
@@ -103,7 +103,7 @@ static struct afs_server *afs_install_server(struct afs_cell *cell,
|
||||
afs_get_cell(cell, afs_cell_trace_get_server);
|
||||
|
||||
exists:
|
||||
afs_use_server(server, true, afs_server_trace_get_install);
|
||||
afs_use_server(server, true, afs_server_trace_use_install);
|
||||
return server;
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ void afs_unuse_server_notime(struct afs_net *net, struct afs_server *server,
|
||||
|
||||
if (atomic_dec_and_test(&server->active)) {
|
||||
if (test_bit(AFS_SERVER_FL_EXPIRED, &server->flags) ||
|
||||
READ_ONCE(server->cell->state) >= AFS_CELL_FAILED)
|
||||
READ_ONCE(server->cell->state) >= AFS_CELL_REMOVING)
|
||||
schedule_work(&server->destroyer);
|
||||
}
|
||||
|
||||
@@ -374,7 +374,7 @@ void afs_unuse_server(struct afs_net *net, struct afs_server *server,
|
||||
|
||||
if (atomic_dec_and_test(&server->active)) {
|
||||
if (!test_bit(AFS_SERVER_FL_EXPIRED, &server->flags) &&
|
||||
READ_ONCE(server->cell->state) < AFS_CELL_FAILED) {
|
||||
READ_ONCE(server->cell->state) < AFS_CELL_REMOVING) {
|
||||
time64_t unuse_time = ktime_get_real_seconds();
|
||||
|
||||
server->unuse_time = unuse_time;
|
||||
@@ -424,7 +424,7 @@ static bool afs_has_server_expired(const struct afs_server *server)
|
||||
return false;
|
||||
|
||||
if (server->cell->net->live ||
|
||||
server->cell->state >= AFS_CELL_FAILED) {
|
||||
server->cell->state >= AFS_CELL_REMOVING) {
|
||||
trace_afs_server(server->debug_id, refcount_read(&server->ref),
|
||||
0, afs_server_trace_purging);
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ static bool afs_start_vl_iteration(struct afs_vl_cursor *vc)
|
||||
cell->dns_expiry <= ktime_get_real_seconds()) {
|
||||
dns_lookup_count = smp_load_acquire(&cell->dns_lookup_count);
|
||||
set_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags);
|
||||
afs_queue_cell(cell, afs_cell_trace_get_queue_dns);
|
||||
afs_queue_cell(cell, afs_cell_trace_queue_dns);
|
||||
|
||||
if (cell->dns_source == DNS_RECORD_UNAVAILABLE) {
|
||||
if (wait_var_event_interruptible(
|
||||
|
||||
+11
-12
@@ -131,7 +131,6 @@ enum yfs_cm_operation {
|
||||
EM(afs_server_trace_destroy, "DESTROY ") \
|
||||
EM(afs_server_trace_free, "FREE ") \
|
||||
EM(afs_server_trace_gc, "GC ") \
|
||||
EM(afs_server_trace_get_install, "GET inst ") \
|
||||
EM(afs_server_trace_get_probe, "GET probe") \
|
||||
EM(afs_server_trace_purging, "PURGE ") \
|
||||
EM(afs_server_trace_put_cbi, "PUT cbi ") \
|
||||
@@ -149,6 +148,7 @@ enum yfs_cm_operation {
|
||||
EM(afs_server_trace_use_cm_call, "USE cm-cl") \
|
||||
EM(afs_server_trace_use_get_caps, "USE gcaps") \
|
||||
EM(afs_server_trace_use_give_up_cb, "USE gvupc") \
|
||||
EM(afs_server_trace_use_install, "USE inst ") \
|
||||
E_(afs_server_trace_wait_create, "WAIT crt ")
|
||||
|
||||
#define afs_volume_traces \
|
||||
@@ -171,37 +171,36 @@ enum yfs_cm_operation {
|
||||
|
||||
#define afs_cell_traces \
|
||||
EM(afs_cell_trace_alloc, "ALLOC ") \
|
||||
EM(afs_cell_trace_destroy, "DESTROY ") \
|
||||
EM(afs_cell_trace_free, "FREE ") \
|
||||
EM(afs_cell_trace_get_atcell, "GET atcell") \
|
||||
EM(afs_cell_trace_get_queue_dns, "GET q-dns ") \
|
||||
EM(afs_cell_trace_get_queue_manage, "GET q-mng ") \
|
||||
EM(afs_cell_trace_get_queue_new, "GET q-new ") \
|
||||
EM(afs_cell_trace_get_server, "GET server") \
|
||||
EM(afs_cell_trace_get_vol, "GET vol ") \
|
||||
EM(afs_cell_trace_insert, "INSERT ") \
|
||||
EM(afs_cell_trace_manage, "MANAGE ") \
|
||||
EM(afs_cell_trace_purge, "PURGE ") \
|
||||
EM(afs_cell_trace_put_atcell, "PUT atcell") \
|
||||
EM(afs_cell_trace_put_candidate, "PUT candid") \
|
||||
EM(afs_cell_trace_put_destroy, "PUT destry") \
|
||||
EM(afs_cell_trace_put_queue_work, "PUT q-work") \
|
||||
EM(afs_cell_trace_put_queue_fail, "PUT q-fail") \
|
||||
EM(afs_cell_trace_put_final, "PUT final ") \
|
||||
EM(afs_cell_trace_put_server, "PUT server") \
|
||||
EM(afs_cell_trace_put_vol, "PUT vol ") \
|
||||
EM(afs_cell_trace_queue_again, "QUE again ") \
|
||||
EM(afs_cell_trace_queue_dns, "QUE dns ") \
|
||||
EM(afs_cell_trace_queue_new, "QUE new ") \
|
||||
EM(afs_cell_trace_queue_purge, "QUE purge ") \
|
||||
EM(afs_cell_trace_manage, "MANAGE ") \
|
||||
EM(afs_cell_trace_managed, "MANAGED ") \
|
||||
EM(afs_cell_trace_see_source, "SEE source") \
|
||||
EM(afs_cell_trace_see_ws, "SEE ws ") \
|
||||
EM(afs_cell_trace_see_mgmt_timer, "SEE mtimer") \
|
||||
EM(afs_cell_trace_unuse_alias, "UNU alias ") \
|
||||
EM(afs_cell_trace_unuse_check_alias, "UNU chk-al") \
|
||||
EM(afs_cell_trace_unuse_delete, "UNU delete") \
|
||||
EM(afs_cell_trace_unuse_dynroot_mntpt, "UNU dyn-mp") \
|
||||
EM(afs_cell_trace_unuse_fc, "UNU fc ") \
|
||||
EM(afs_cell_trace_unuse_lookup, "UNU lookup") \
|
||||
EM(afs_cell_trace_unuse_lookup_dynroot, "UNU lu-dyn") \
|
||||
EM(afs_cell_trace_unuse_lookup_error, "UNU lu-err") \
|
||||
EM(afs_cell_trace_unuse_mntpt, "UNU mntpt ") \
|
||||
EM(afs_cell_trace_unuse_no_pin, "UNU no-pin") \
|
||||
EM(afs_cell_trace_unuse_parse, "UNU parse ") \
|
||||
EM(afs_cell_trace_unuse_pin, "UNU pin ") \
|
||||
EM(afs_cell_trace_unuse_probe, "UNU probe ") \
|
||||
EM(afs_cell_trace_unuse_sbi, "UNU sbi ") \
|
||||
EM(afs_cell_trace_unuse_ws, "UNU ws ") \
|
||||
EM(afs_cell_trace_use_alias, "USE alias ") \
|
||||
|
||||
Reference in New Issue
Block a user