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 <dhowells@redhat.com>
diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index bd570fa..2224e33 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 30ce4be..9ad39f8 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 613a770..97ec6a7 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 342316a..fbb441d 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 2d90cb7..7cd30ae 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 c934334..677a453 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 33aeb52..d8044be 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 e43f94ec..dd218f3 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 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
 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 ccb7aac..cf7e02d 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 dcb9561..d5ef834 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 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode,
 		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 3d5363e..e2f0e8e 100644
--- a/fs/afs/volume.c
+++ b/fs/afs/volume.c
@@ -154,7 +154,7 @@ struct afs_volume *afs_volume_lookup(struct afs_mount_params *params)
 	up_write(&params->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 @@ struct afs_volume *afs_volume_lookup(struct afs_mount_params *params)
 /*
  * 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;
 }