From 9ed900b1160ef306bc74ad0228d7ab199234c758 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 2 Nov 2017 15:27:46 +0000 Subject: [PATCH] afs: Push the net ns pointer to more places Push the network namespace pointer to more places in AFS, including the afs_server structure (which doesn't hold a ref on the netns). In particular, afs_put_cell() now takes requires a net ns parameter so that it can safely alter the netns after decrementing the cell usage count - the cell will be deallocated by a background thread after being cached for a period, which means that it's not safe to access it after reducing its usage count. Signed-off-by: David Howells --- fs/afs/cell.c | 14 +++++++------- fs/afs/cmservice.c | 2 +- fs/afs/dir.c | 12 ++++++------ fs/afs/inode.c | 2 +- fs/afs/internal.h | 7 ++++--- fs/afs/proc.c | 2 +- fs/afs/server.c | 7 +++---- fs/afs/super.c | 12 ++++++------ fs/afs/vlocation.c | 6 +++--- fs/afs/vnode.c | 28 ++++++++++++++-------------- fs/afs/volume.c | 20 ++++++++++---------- 11 files changed, 56 insertions(+), 56 deletions(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index bd570fa539a0..2224e335eed7 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -251,7 +251,7 @@ int afs_cell_init(struct afs_net *net, char *rootcell) old_root = net->ws_cell; net->ws_cell = new_root; write_unlock(&net->cells_lock); - afs_put_cell(old_root); + afs_put_cell(net, old_root); _leave(" = 0"); return 0; @@ -336,7 +336,7 @@ struct afs_cell *afs_get_cell_maybe(struct afs_cell *cell) /* * destroy a cell record */ -void afs_put_cell(struct afs_cell *cell) +void afs_put_cell(struct afs_net *net, struct afs_cell *cell) { if (!cell) return; @@ -347,10 +347,10 @@ void afs_put_cell(struct afs_cell *cell) /* to prevent a race, the decrement and the dequeue must be effectively * atomic */ - write_lock(&cell->net->cells_lock); + write_lock(&net->cells_lock); if (likely(!atomic_dec_and_test(&cell->usage))) { - write_unlock(&cell->net->cells_lock); + write_unlock(&net->cells_lock); _leave(""); return; } @@ -358,9 +358,9 @@ void afs_put_cell(struct afs_cell *cell) ASSERT(list_empty(&cell->servers)); ASSERT(list_empty(&cell->vl_list)); - wake_up(&cell->net->cells_freeable_wq); + wake_up(&net->cells_freeable_wq); - write_unlock(&cell->net->cells_lock); + write_unlock(&net->cells_lock); _leave(" [unused]"); } @@ -424,7 +424,7 @@ void afs_cell_purge(struct afs_net *net) _enter(""); - afs_put_cell(net->ws_cell); + afs_put_cell(net, net->ws_cell); down_write(&net->cells_sem); diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 30ce4be4165f..9ad39f8a7e87 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -151,7 +151,7 @@ static void afs_cm_destructor(struct afs_call *call) afs_break_callbacks(call->server, call->count, call->request); } - afs_put_server(call->server); + afs_put_server(call->net, call->server); call->server = NULL; kfree(call->buffer); call->buffer = NULL; diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 613a77058263..97ec6a74589e 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -771,7 +771,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) vnode->update_cnt++; spin_unlock(&vnode->lock); afs_vnode_finalise_status_update(vnode, server); - afs_put_server(server); + afs_put_server(afs_i2net(dir), server); d_instantiate(dentry, inode); if (d_unhashed(dentry)) { @@ -783,7 +783,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) return 0; iget_error: - afs_put_server(server); + afs_put_server(afs_i2net(dir), server); mkdir_error: key_put(key); error: @@ -948,7 +948,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, vnode->update_cnt++; spin_unlock(&vnode->lock); afs_vnode_finalise_status_update(vnode, server); - afs_put_server(server); + afs_put_server(afs_i2net(dir), server); d_instantiate(dentry, inode); if (d_unhashed(dentry)) { @@ -960,7 +960,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, return 0; iget_error: - afs_put_server(server); + afs_put_server(afs_i2net(dir), server); create_error: key_put(key); error: @@ -1060,7 +1060,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, vnode->update_cnt++; spin_unlock(&vnode->lock); afs_vnode_finalise_status_update(vnode, server); - afs_put_server(server); + afs_put_server(afs_i2net(dir), server); d_instantiate(dentry, inode); if (d_unhashed(dentry)) { @@ -1072,7 +1072,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, return 0; iget_error: - afs_put_server(server); + afs_put_server(afs_i2net(dir), server); create_error: key_put(key); error: diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 342316a9e3e0..fbb441d25022 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -437,7 +437,7 @@ void afs_evict_inode(struct inode *inode) spin_lock(&vnode->server->fs_lock); rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes); spin_unlock(&vnode->server->fs_lock); - afs_put_server(vnode->server); + afs_put_server(afs_i2net(inode), vnode->server); vnode->server = NULL; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 2d90cb7605f3..7cd30ae71f91 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -335,6 +335,7 @@ struct afs_server { atomic_t usage; time64_t time_of_death; /* time at which put reduced usage to 0 */ struct in_addr addr; /* server address */ + struct afs_net *net; /* Network namespace in which the server resides */ struct afs_cell *cell; /* cell in which server resides */ struct list_head link; /* link in cell's server list */ struct list_head grave; /* link in master graveyard list */ @@ -513,7 +514,7 @@ extern int afs_cell_init(struct afs_net *, char *); extern struct afs_cell *afs_cell_create(struct afs_net *, const char *, unsigned, char *, bool); extern struct afs_cell *afs_cell_lookup(struct afs_net *, const char *, unsigned, bool); extern struct afs_cell *afs_grab_cell(struct afs_cell *); -extern void afs_put_cell(struct afs_cell *); +extern void afs_put_cell(struct afs_net *, struct afs_cell *); extern void __net_exit afs_cell_purge(struct afs_net *); /* @@ -713,7 +714,7 @@ extern struct afs_server *afs_lookup_server(struct afs_cell *, const struct in_addr *); extern struct afs_server *afs_find_server(struct afs_net *, const struct sockaddr_rxrpc *); -extern void afs_put_server(struct afs_server *); +extern void afs_put_server(struct afs_net *, struct afs_server *); extern void afs_reap_server(struct work_struct *); extern void __net_exit afs_purge_servers(struct afs_net *); @@ -802,7 +803,7 @@ static inline struct afs_volume *afs_get_volume(struct afs_volume *volume) return volume; } -extern void afs_put_volume(struct afs_net *, struct afs_volume *); +extern void afs_put_volume(struct afs_cell *, struct afs_volume *); extern struct afs_volume *afs_volume_lookup(struct afs_mount_params *); extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *); extern int afs_volume_release_fileserver(struct afs_vnode *, diff --git a/fs/afs/proc.c b/fs/afs/proc.c index c93433460348..677a453b08bf 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -285,7 +285,7 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, goto done; } - afs_put_cell(cell); + afs_put_cell(net, cell); printk("kAFS: Added new cell '%s'\n", name); } else { goto inval; diff --git a/fs/afs/server.c b/fs/afs/server.c index 33aeb527ac7e..d8044be913f0 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -85,6 +85,7 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell, server = kzalloc(sizeof(struct afs_server), GFP_KERNEL); if (server) { atomic_set(&server->usage, 1); + server->net = cell->net; server->cell = cell; INIT_LIST_HEAD(&server->link); @@ -245,10 +246,8 @@ static void afs_set_server_timer(struct afs_net *net, time64_t delay) * destroy a server record * - removes from the cell list */ -void afs_put_server(struct afs_server *server) +void afs_put_server(struct afs_net *net, struct afs_server *server) { - struct afs_net *net = server->cell->net; - if (!server) return; @@ -290,7 +289,7 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server) ASSERTCMP(server->cb_break_head, ==, server->cb_break_tail); ASSERTCMP(atomic_read(&server->cb_break_n), ==, 0); - afs_put_cell(server->cell); + afs_put_cell(server->net, server->cell); kfree(server); afs_dec_servers_outstanding(net); } diff --git a/fs/afs/super.c b/fs/afs/super.c index e43f94ecc391..dd218f370359 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -206,7 +206,7 @@ static int afs_parse_options(struct afs_mount_params *params, false); if (IS_ERR(cell)) return PTR_ERR(cell); - afs_put_cell(params->cell); + afs_put_cell(params->net, params->cell); params->cell = cell; break; @@ -314,7 +314,7 @@ static int afs_parse_device_name(struct afs_mount_params *params, cellnamesz, cellnamesz, cellname ?: ""); return PTR_ERR(cell); } - afs_put_cell(params->cell); + afs_put_cell(params->net, params->cell); params->cell = cell; } @@ -409,8 +409,8 @@ static struct afs_super_info *afs_alloc_sbi(struct afs_mount_params *params) static void afs_destroy_sbi(struct afs_super_info *as) { if (as) { - afs_put_volume(as->net, as->volume); - afs_put_cell(as->cell); + afs_put_volume(as->cell, as->volume); + afs_put_cell(as->net, as->cell); afs_put_net(as->net); kfree(as); } @@ -494,7 +494,7 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, as = NULL; } - afs_put_cell(params.cell); + afs_put_cell(params.net, params.cell); key_put(params.key); _leave(" = 0 [%p]", sb); return dget(sb->s_root); @@ -504,7 +504,7 @@ error_sb: error_as: afs_destroy_sbi(as); error: - afs_put_cell(params.cell); + afs_put_cell(params.net, params.cell); key_put(params.key); _leave(" = %d", ret); return ERR_PTR(ret); diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c index ccb7aacfbeca..cf7e02d5fa3f 100644 --- a/fs/afs/vlocation.c +++ b/fs/afs/vlocation.c @@ -518,14 +518,14 @@ void afs_put_vlocation(struct afs_net *net, struct afs_vlocation *vl) /* * destroy a dead volume location record */ -static void afs_vlocation_destroy(struct afs_vlocation *vl) +static void afs_vlocation_destroy(struct afs_net *net, struct afs_vlocation *vl) { _enter("%p", vl); #ifdef CONFIG_AFS_FSCACHE fscache_relinquish_cookie(vl->cache, 0); #endif - afs_put_cell(vl->cell); + afs_put_cell(net, vl->cell); kfree(vl); } @@ -580,7 +580,7 @@ void afs_vlocation_reaper(struct work_struct *work) while (!list_empty(&corpses)) { vl = list_entry(corpses.next, struct afs_vlocation, grave); list_del(&vl->grave); - afs_vlocation_destroy(vl); + afs_vlocation_destroy(net, vl); } _leave(""); diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c index dcb956143c86..d5ef834ba4ac 100644 --- a/fs/afs/vnode.c +++ b/fs/afs/vnode.c @@ -73,7 +73,7 @@ static void afs_install_vnode(struct afs_vnode *vnode, afs_get_server(server); vnode->server = server; - afs_put_server(old_server); + afs_put_server(afs_v2net(vnode), old_server); /* insert into the server's vnode tree in FID order */ spin_lock(&server->fs_lock); @@ -196,7 +196,7 @@ static void afs_vnode_deleted_remotely(struct afs_vnode *vnode) spin_unlock(&server->fs_lock); vnode->server = NULL; - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); } else { ASSERT(!vnode->cb_promised); } @@ -225,7 +225,7 @@ void afs_vnode_finalise_status_update(struct afs_vnode *vnode, spin_unlock(&vnode->lock); wake_up_all(&vnode->update_waitq); - afs_put_server(oldserver); + afs_put_server(afs_v2net(vnode), oldserver); _leave(""); } @@ -368,7 +368,7 @@ get_anyway: if (auth_vnode) afs_cache_permit(vnode, key, acl_order); afs_vnode_finalise_status_update(vnode, server); - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); } else { _debug("failed [%d]", ret); afs_vnode_status_update_failed(vnode, ret); @@ -428,7 +428,7 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key, /* adjust the flags */ if (ret == 0) { afs_vnode_finalise_status_update(vnode, server); - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); } else { afs_vnode_status_update_failed(vnode, ret); } @@ -540,7 +540,7 @@ int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name, /* adjust the flags */ if (ret == 0) { afs_vnode_finalise_status_update(vnode, server); - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); } else { afs_vnode_status_update_failed(vnode, ret); } @@ -603,7 +603,7 @@ int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode, if (ret == 0) { afs_vnode_finalise_status_update(vnode, server); afs_vnode_finalise_status_update(dvnode, server); - afs_put_server(server); + afs_put_server(afs_v2net(dvnode), server); } else { afs_vnode_status_update_failed(vnode, ret); afs_vnode_status_update_failed(dvnode, ret); @@ -738,7 +738,7 @@ int afs_vnode_rename(struct afs_vnode *orig_dvnode, afs_vnode_finalise_status_update(orig_dvnode, server); if (new_dvnode != orig_dvnode) afs_vnode_finalise_status_update(new_dvnode, server); - afs_put_server(server); + afs_put_server(afs_v2net(orig_dvnode), server); } else { afs_vnode_status_update_failed(orig_dvnode, ret); if (new_dvnode != orig_dvnode) @@ -802,7 +802,7 @@ int afs_vnode_store_data(struct afs_writeback *wb, pgoff_t first, pgoff_t last, /* adjust the flags */ if (ret == 0) { afs_vnode_finalise_status_update(vnode, server); - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); } else { afs_vnode_status_update_failed(vnode, ret); } @@ -854,7 +854,7 @@ int afs_vnode_setattr(struct afs_vnode *vnode, struct key *key, /* adjust the flags */ if (ret == 0) { afs_vnode_finalise_status_update(vnode, server); - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); } else { afs_vnode_status_update_failed(vnode, ret); } @@ -900,7 +900,7 @@ int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key, /* adjust the flags */ if (ret == 0) - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); _leave(" = %d", ret); return ret; @@ -939,7 +939,7 @@ int afs_vnode_set_lock(struct afs_vnode *vnode, struct key *key, /* adjust the flags */ if (ret == 0) - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); _leave(" = %d", ret); return ret; @@ -977,7 +977,7 @@ int afs_vnode_extend_lock(struct afs_vnode *vnode, struct key *key) /* adjust the flags */ if (ret == 0) - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); _leave(" = %d", ret); return ret; @@ -1015,7 +1015,7 @@ int afs_vnode_release_lock(struct afs_vnode *vnode, struct key *key) /* adjust the flags */ if (ret == 0) - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); _leave(" = %d", ret); return ret; diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 3d5363e0b7e1..e2f0e8ec527d 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -154,7 +154,7 @@ error_discard: up_write(¶ms->cell->vl_sem); for (loop = volume->nservers - 1; loop >= 0; loop--) - afs_put_server(volume->servers[loop]); + afs_put_server(params->net, volume->servers[loop]); kfree(volume); goto error; @@ -163,7 +163,7 @@ error_discard: /* * destroy a volume record */ -void afs_put_volume(struct afs_net *net, struct afs_volume *volume) +void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume) { struct afs_vlocation *vlocation; int loop; @@ -179,7 +179,7 @@ void afs_put_volume(struct afs_net *net, struct afs_volume *volume) /* to prevent a race, the decrement and the dequeue must be effectively * atomic */ - down_write(&vlocation->cell->vl_sem); + down_write(&cell->vl_sem); if (likely(!atomic_dec_and_test(&volume->usage))) { up_write(&vlocation->cell->vl_sem); @@ -189,16 +189,16 @@ void afs_put_volume(struct afs_net *net, struct afs_volume *volume) vlocation->vols[volume->type] = NULL; - up_write(&vlocation->cell->vl_sem); + up_write(&cell->vl_sem); /* finish cleaning up the volume */ #ifdef CONFIG_AFS_FSCACHE fscache_relinquish_cookie(volume->cache, 0); #endif - afs_put_vlocation(net, vlocation); + afs_put_vlocation(cell->net, vlocation); for (loop = volume->nservers - 1; loop >= 0; loop--) - afs_put_server(volume->servers[loop]); + afs_put_server(cell->net, volume->servers[loop]); kfree(volume); @@ -336,7 +336,7 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, sizeof(volume->servers[loop]) * (volume->nservers - loop)); volume->servers[volume->nservers] = NULL; - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); volume->rjservers++; if (volume->nservers > 0) @@ -350,7 +350,7 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, * no longer registered */ up_write(&volume->server_sem); - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); _leave(" [completely rejected]"); return 1; @@ -379,7 +379,7 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, case -ENOMEM: case -ENONET: /* tell the caller to accept the result */ - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); _leave(" [local failure]"); return 1; } @@ -388,7 +388,7 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, try_next_server_upw: up_write(&volume->server_sem); try_next_server: - afs_put_server(server); + afs_put_server(afs_v2net(vnode), server); _leave(" [try next server]"); return 0; }