xref: /openbmc/linux/fs/afs/proc.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ec26815aSDavid Howells /* /proc interface for AFS
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
51da177e4SLinus Torvalds  * Written by David Howells (dhowells@redhat.com)
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds #include <linux/slab.h>
91da177e4SLinus Torvalds #include <linux/module.h>
101da177e4SLinus Torvalds #include <linux/proc_fs.h>
111da177e4SLinus Torvalds #include <linux/seq_file.h>
12e8edc6e0SAlexey Dobriyan #include <linux/sched.h>
137c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
141da177e4SLinus Torvalds #include "internal.h"
151da177e4SLinus Torvalds 
160a5143f2SDavid Howells struct afs_vl_seq_net_private {
170a5143f2SDavid Howells 	struct seq_net_private		seq;	/* Must be first */
180a5143f2SDavid Howells 	struct afs_vlserver_list	*vllist;
190a5143f2SDavid Howells };
200a5143f2SDavid Howells 
afs_seq2net(struct seq_file * m)21f044c884SDavid Howells static inline struct afs_net *afs_seq2net(struct seq_file *m)
22f044c884SDavid Howells {
235b86d4ffSDavid Howells 	return afs_net(seq_file_net(m));
245b86d4ffSDavid Howells }
255b86d4ffSDavid Howells 
afs_seq2net_single(struct seq_file * m)265b86d4ffSDavid Howells static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
275b86d4ffSDavid Howells {
285b86d4ffSDavid Howells 	return afs_net(seq_file_single_net(m));
29f044c884SDavid Howells }
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds /*
325d9de25dSDavid Howells  * Display the list of cells known to the namespace.
33f0691689SDavid Howells  */
afs_proc_cells_show(struct seq_file * m,void * v)34f0691689SDavid Howells static int afs_proc_cells_show(struct seq_file *m, void *v)
35f0691689SDavid Howells {
36ded2f4c5SDavid Howells 	struct afs_vlserver_list *vllist;
37ded2f4c5SDavid Howells 	struct afs_cell *cell;
38f0691689SDavid Howells 
396b3944e4SDavid Howells 	if (v == SEQ_START_TOKEN) {
40f0691689SDavid Howells 		/* display header on line 1 */
4188c853c3SDavid Howells 		seq_puts(m, "USE ACT    TTL SV ST NAME\n");
42f0691689SDavid Howells 		return 0;
43f0691689SDavid Howells 	}
44f0691689SDavid Howells 
45ded2f4c5SDavid Howells 	cell = list_entry(v, struct afs_cell, proc_link);
46ded2f4c5SDavid Howells 	vllist = rcu_dereference(cell->vl_servers);
47ded2f4c5SDavid Howells 
48f0691689SDavid Howells 	/* display one cell per line on subsequent lines */
4988c853c3SDavid Howells 	seq_printf(m, "%3u %3u %6lld %2u %2u %s\n",
50*c56f9ec8SDavid Howells 		   refcount_read(&cell->ref),
5188c853c3SDavid Howells 		   atomic_read(&cell->active),
52ded2f4c5SDavid Howells 		   cell->dns_expiry - ktime_get_real_seconds(),
5388c853c3SDavid Howells 		   vllist ? vllist->nr_servers : 0,
548a070a96SDavid Howells 		   cell->state,
55ded2f4c5SDavid Howells 		   cell->name);
56f0691689SDavid Howells 	return 0;
57f0691689SDavid Howells }
58f0691689SDavid Howells 
afs_proc_cells_start(struct seq_file * m,loff_t * _pos)591da177e4SLinus Torvalds static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
60fe342cf7SDavid Howells 	__acquires(rcu)
611da177e4SLinus Torvalds {
62989782dcSDavid Howells 	rcu_read_lock();
636b3944e4SDavid Howells 	return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos);
64ec26815aSDavid Howells }
651da177e4SLinus Torvalds 
afs_proc_cells_next(struct seq_file * m,void * v,loff_t * pos)66f044c884SDavid Howells static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
671da177e4SLinus Torvalds {
686b3944e4SDavid Howells 	return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos);
69ec26815aSDavid Howells }
701da177e4SLinus Torvalds 
afs_proc_cells_stop(struct seq_file * m,void * v)71f044c884SDavid Howells static void afs_proc_cells_stop(struct seq_file *m, void *v)
72fe342cf7SDavid Howells 	__releases(rcu)
731da177e4SLinus Torvalds {
74989782dcSDavid Howells 	rcu_read_unlock();
75ec26815aSDavid Howells }
761da177e4SLinus Torvalds 
775d9de25dSDavid Howells static const struct seq_operations afs_proc_cells_ops = {
785d9de25dSDavid Howells 	.start	= afs_proc_cells_start,
795d9de25dSDavid Howells 	.next	= afs_proc_cells_next,
805d9de25dSDavid Howells 	.stop	= afs_proc_cells_stop,
815d9de25dSDavid Howells 	.show	= afs_proc_cells_show,
825d9de25dSDavid Howells };
835d9de25dSDavid Howells 
841da177e4SLinus Torvalds /*
851da177e4SLinus Torvalds  * handle writes to /proc/fs/afs/cells
861da177e4SLinus Torvalds  * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
871da177e4SLinus Torvalds  */
afs_proc_cells_write(struct file * file,char * buf,size_t size)885b86d4ffSDavid Howells static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
891da177e4SLinus Torvalds {
905b86d4ffSDavid Howells 	struct seq_file *m = file->private_data;
915b86d4ffSDavid Howells 	struct afs_net *net = afs_seq2net(m);
925b86d4ffSDavid Howells 	char *name, *args;
931da177e4SLinus Torvalds 	int ret;
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	/* trim to first NL */
965b86d4ffSDavid Howells 	name = memchr(buf, '\n', size);
971da177e4SLinus Torvalds 	if (name)
981da177e4SLinus Torvalds 		*name = 0;
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 	/* split into command, name and argslist */
1015b86d4ffSDavid Howells 	name = strchr(buf, ' ');
1021da177e4SLinus Torvalds 	if (!name)
1031da177e4SLinus Torvalds 		goto inval;
1041da177e4SLinus Torvalds 	do {
1051da177e4SLinus Torvalds 		*name++ = 0;
1061da177e4SLinus Torvalds 	} while(*name == ' ');
1071da177e4SLinus Torvalds 	if (!*name)
1081da177e4SLinus Torvalds 		goto inval;
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds 	args = strchr(name, ' ');
111ecfe951fSDavid Howells 	if (args) {
1121da177e4SLinus Torvalds 		do {
1131da177e4SLinus Torvalds 			*args++ = 0;
1141da177e4SLinus Torvalds 		} while(*args == ' ');
1151da177e4SLinus Torvalds 		if (!*args)
1161da177e4SLinus Torvalds 			goto inval;
117ecfe951fSDavid Howells 	}
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	/* determine command to perform */
1205b86d4ffSDavid Howells 	_debug("cmd=%s name=%s args=%s", buf, name, args);
1211da177e4SLinus Torvalds 
1225b86d4ffSDavid Howells 	if (strcmp(buf, "add") == 0) {
1231da177e4SLinus Torvalds 		struct afs_cell *cell;
1241da177e4SLinus Torvalds 
125989782dcSDavid Howells 		cell = afs_lookup_cell(net, name, strlen(name), args, true);
12608e0e7c8SDavid Howells 		if (IS_ERR(cell)) {
12708e0e7c8SDavid Howells 			ret = PTR_ERR(cell);
12808e0e7c8SDavid Howells 			goto done;
12908e0e7c8SDavid Howells 		}
13008e0e7c8SDavid Howells 
13117814aefSDavid Howells 		if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
132dca54a7bSDavid Howells 			afs_unuse_cell(net, cell, afs_cell_trace_unuse_no_pin);
133ec26815aSDavid Howells 	} else {
1341da177e4SLinus Torvalds 		goto inval;
1351da177e4SLinus Torvalds 	}
1361da177e4SLinus Torvalds 
1375b86d4ffSDavid Howells 	ret = 0;
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds done:
1401da177e4SLinus Torvalds 	_leave(" = %d", ret);
1411da177e4SLinus Torvalds 	return ret;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds inval:
1441da177e4SLinus Torvalds 	ret = -EINVAL;
1451da177e4SLinus Torvalds 	printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
1461da177e4SLinus Torvalds 	goto done;
147ec26815aSDavid Howells }
1481da177e4SLinus Torvalds 
1495d9de25dSDavid Howells /*
1505b86d4ffSDavid Howells  * Display the name of the current workstation cell.
1515d9de25dSDavid Howells  */
afs_proc_rootcell_show(struct seq_file * m,void * v)1525b86d4ffSDavid Howells static int afs_proc_rootcell_show(struct seq_file *m, void *v)
1531da177e4SLinus Torvalds {
15437ab6368SDavid Howells 	struct afs_cell *cell;
1555b86d4ffSDavid Howells 	struct afs_net *net;
15637ab6368SDavid Howells 
1575b86d4ffSDavid Howells 	net = afs_seq2net_single(m);
15888c853c3SDavid Howells 	down_read(&net->cells_lock);
15988c853c3SDavid Howells 	cell = net->ws_cell;
1605b86d4ffSDavid Howells 	if (cell)
1615b86d4ffSDavid Howells 		seq_printf(m, "%s\n", cell->name);
16288c853c3SDavid Howells 	up_read(&net->cells_lock);
16337ab6368SDavid Howells 	return 0;
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds /*
1675d9de25dSDavid Howells  * Set the current workstation cell and optionally supply its list of volume
1685d9de25dSDavid Howells  * location servers.
1695d9de25dSDavid Howells  *
1705d9de25dSDavid Howells  *	echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
1711da177e4SLinus Torvalds  */
afs_proc_rootcell_write(struct file * file,char * buf,size_t size)1725b86d4ffSDavid Howells static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
1731da177e4SLinus Torvalds {
1745b86d4ffSDavid Howells 	struct seq_file *m = file->private_data;
1755b86d4ffSDavid Howells 	struct afs_net *net = afs_seq2net_single(m);
1765b86d4ffSDavid Howells 	char *s;
1771da177e4SLinus Torvalds 	int ret;
1781da177e4SLinus Torvalds 
1796f8880d8SDavid Howells 	ret = -EINVAL;
1805b86d4ffSDavid Howells 	if (buf[0] == '.')
1816f8880d8SDavid Howells 		goto out;
1825b86d4ffSDavid Howells 	if (memchr(buf, '/', size))
1836f8880d8SDavid Howells 		goto out;
1846f8880d8SDavid Howells 
1851da177e4SLinus Torvalds 	/* trim to first NL */
1865b86d4ffSDavid Howells 	s = memchr(buf, '\n', size);
1871da177e4SLinus Torvalds 	if (s)
1881da177e4SLinus Torvalds 		*s = 0;
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds 	/* determine command to perform */
1915b86d4ffSDavid Howells 	_debug("rootcell=%s", buf);
1921da177e4SLinus Torvalds 
1935b86d4ffSDavid Howells 	ret = afs_cell_init(net, buf);
1941da177e4SLinus Torvalds 
1956f8880d8SDavid Howells out:
1961da177e4SLinus Torvalds 	_leave(" = %d", ret);
1971da177e4SLinus Torvalds 	return ret;
198ec26815aSDavid Howells }
1991da177e4SLinus Torvalds 
200f0691689SDavid Howells static const char afs_vol_types[3][3] = {
201f0691689SDavid Howells 	[AFSVL_RWVOL]	= "RW",
202f0691689SDavid Howells 	[AFSVL_ROVOL]	= "RO",
203f0691689SDavid Howells 	[AFSVL_BACKVOL]	= "BK",
204f0691689SDavid Howells };
205f0691689SDavid Howells 
206f0691689SDavid Howells /*
2075d9de25dSDavid Howells  * Display the list of volumes known to a cell.
208f0691689SDavid Howells  */
afs_proc_cell_volumes_show(struct seq_file * m,void * v)209f0691689SDavid Howells static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
210f0691689SDavid Howells {
21120325960SDavid Howells 	struct afs_volume *vol = hlist_entry(v, struct afs_volume, proc_link);
212f0691689SDavid Howells 
213f0691689SDavid Howells 	/* Display header on line 1 */
21420325960SDavid Howells 	if (v == SEQ_START_TOKEN) {
21550559800SDavid Howells 		seq_puts(m, "USE VID      TY NAME\n");
216f0691689SDavid Howells 		return 0;
217f0691689SDavid Howells 	}
218f0691689SDavid Howells 
21950559800SDavid Howells 	seq_printf(m, "%3d %08llx %s %s\n",
220*c56f9ec8SDavid Howells 		   refcount_read(&vol->ref), vol->vid,
22150559800SDavid Howells 		   afs_vol_types[vol->type],
22250559800SDavid Howells 		   vol->name);
223f0691689SDavid Howells 
224f0691689SDavid Howells 	return 0;
225f0691689SDavid Howells }
226f0691689SDavid Howells 
afs_proc_cell_volumes_start(struct seq_file * m,loff_t * _pos)2271da177e4SLinus Torvalds static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
228fe342cf7SDavid Howells 	__acquires(cell->proc_lock)
2291da177e4SLinus Torvalds {
230359745d7SMuchun Song 	struct afs_cell *cell = pde_data(file_inode(m->file));
2311da177e4SLinus Torvalds 
23220325960SDavid Howells 	rcu_read_lock();
23320325960SDavid Howells 	return seq_hlist_start_head_rcu(&cell->proc_volumes, *_pos);
234ec26815aSDavid Howells }
2351da177e4SLinus Torvalds 
afs_proc_cell_volumes_next(struct seq_file * m,void * v,loff_t * _pos)2365b86d4ffSDavid Howells static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
2371da177e4SLinus Torvalds 					loff_t *_pos)
2381da177e4SLinus Torvalds {
239359745d7SMuchun Song 	struct afs_cell *cell = pde_data(file_inode(m->file));
2401da177e4SLinus Torvalds 
24120325960SDavid Howells 	return seq_hlist_next_rcu(v, &cell->proc_volumes, _pos);
242ec26815aSDavid Howells }
2431da177e4SLinus Torvalds 
afs_proc_cell_volumes_stop(struct seq_file * m,void * v)2445b86d4ffSDavid Howells static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
245fe342cf7SDavid Howells 	__releases(cell->proc_lock)
2461da177e4SLinus Torvalds {
24720325960SDavid Howells 	rcu_read_unlock();
248ec26815aSDavid Howells }
2491da177e4SLinus Torvalds 
2505d9de25dSDavid Howells static const struct seq_operations afs_proc_cell_volumes_ops = {
2515d9de25dSDavid Howells 	.start	= afs_proc_cell_volumes_start,
2525d9de25dSDavid Howells 	.next	= afs_proc_cell_volumes_next,
2535d9de25dSDavid Howells 	.stop	= afs_proc_cell_volumes_stop,
2545d9de25dSDavid Howells 	.show	= afs_proc_cell_volumes_show,
2555d9de25dSDavid Howells };
2565d9de25dSDavid Howells 
2570a5143f2SDavid Howells static const char *const dns_record_sources[NR__dns_record_source + 1] = {
2580a5143f2SDavid Howells 	[DNS_RECORD_UNAVAILABLE]	= "unav",
2590a5143f2SDavid Howells 	[DNS_RECORD_FROM_CONFIG]	= "cfg",
2600a5143f2SDavid Howells 	[DNS_RECORD_FROM_DNS_A]		= "A",
2610a5143f2SDavid Howells 	[DNS_RECORD_FROM_DNS_AFSDB]	= "AFSDB",
2620a5143f2SDavid Howells 	[DNS_RECORD_FROM_DNS_SRV]	= "SRV",
2630a5143f2SDavid Howells 	[DNS_RECORD_FROM_NSS]		= "nss",
2640a5143f2SDavid Howells 	[NR__dns_record_source]		= "[weird]"
2650a5143f2SDavid Howells };
2660a5143f2SDavid Howells 
2670a5143f2SDavid Howells static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = {
2680a5143f2SDavid Howells 	[DNS_LOOKUP_NOT_DONE]		= "no-lookup",
2690a5143f2SDavid Howells 	[DNS_LOOKUP_GOOD]		= "good",
2700a5143f2SDavid Howells 	[DNS_LOOKUP_GOOD_WITH_BAD]	= "good/bad",
2710a5143f2SDavid Howells 	[DNS_LOOKUP_BAD]		= "bad",
2720a5143f2SDavid Howells 	[DNS_LOOKUP_GOT_NOT_FOUND]	= "not-found",
2730a5143f2SDavid Howells 	[DNS_LOOKUP_GOT_LOCAL_FAILURE]	= "local-failure",
2740a5143f2SDavid Howells 	[DNS_LOOKUP_GOT_TEMP_FAILURE]	= "temp-failure",
2750a5143f2SDavid Howells 	[DNS_LOOKUP_GOT_NS_FAILURE]	= "ns-failure",
2760a5143f2SDavid Howells 	[NR__dns_lookup_status]		= "[weird]"
2770a5143f2SDavid Howells };
2780a5143f2SDavid Howells 
2791da177e4SLinus Torvalds /*
2805d9de25dSDavid Howells  * Display the list of Volume Location servers we're using for a cell.
2811da177e4SLinus Torvalds  */
afs_proc_cell_vlservers_show(struct seq_file * m,void * v)282f0691689SDavid Howells static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
2831da177e4SLinus Torvalds {
2840a5143f2SDavid Howells 	const struct afs_vl_seq_net_private *priv = m->private;
2850a5143f2SDavid Howells 	const struct afs_vlserver_list *vllist = priv->vllist;
2860a5143f2SDavid Howells 	const struct afs_vlserver_entry *entry;
2870a5143f2SDavid Howells 	const struct afs_vlserver *vlserver;
2880a5143f2SDavid Howells 	const struct afs_addr_list *alist;
2890a5143f2SDavid Howells 	int i;
2901da177e4SLinus Torvalds 
2910a5143f2SDavid Howells 	if (v == SEQ_START_TOKEN) {
2920a5143f2SDavid Howells 		seq_printf(m, "# source %s, status %s\n",
293ca1cbbdcSDavid Howells 			   dns_record_sources[vllist ? vllist->source : 0],
294ca1cbbdcSDavid Howells 			   dns_lookup_statuses[vllist ? vllist->status : 0]);
2951da177e4SLinus Torvalds 		return 0;
2961da177e4SLinus Torvalds 	}
2971da177e4SLinus Torvalds 
2980a5143f2SDavid Howells 	entry = v;
2990a5143f2SDavid Howells 	vlserver = entry->server;
3000a5143f2SDavid Howells 	alist = rcu_dereference(vlserver->addresses);
3010a5143f2SDavid Howells 
3020a5143f2SDavid Howells 	seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n",
3030a5143f2SDavid Howells 		   vlserver->name, entry->priority, entry->weight,
3040a5143f2SDavid Howells 		   dns_record_sources[alist ? alist->source : entry->source],
3050a5143f2SDavid Howells 		   dns_lookup_statuses[alist ? alist->status : entry->status]);
3060a5143f2SDavid Howells 	if (alist) {
3070a5143f2SDavid Howells 		for (i = 0; i < alist->nr_addrs; i++)
3080a5143f2SDavid Howells 			seq_printf(m, " %c %pISpc\n",
3093bf0fb6fSDavid Howells 				   alist->preferred == i ? '>' : '-',
3100a5143f2SDavid Howells 				   &alist->addrs[i].transport);
3110a5143f2SDavid Howells 	}
312b95b3094SDavid Howells 	seq_printf(m, " info: fl=%lx rtt=%d\n", vlserver->flags, vlserver->rtt);
313fb72cd3dSDavid Howells 	seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
314fb72cd3dSDavid Howells 		   vlserver->probe.flags, vlserver->probe.error,
315fb72cd3dSDavid Howells 		   vlserver->probe.abort_code,
316fb72cd3dSDavid Howells 		   atomic_read(&vlserver->probe_outstanding));
3171da177e4SLinus Torvalds 	return 0;
318ec26815aSDavid Howells }
3191da177e4SLinus Torvalds 
afs_proc_cell_vlservers_start(struct seq_file * m,loff_t * _pos)3201da177e4SLinus Torvalds static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
321fe342cf7SDavid Howells 	__acquires(rcu)
3221da177e4SLinus Torvalds {
3230a5143f2SDavid Howells 	struct afs_vl_seq_net_private *priv = m->private;
3240a5143f2SDavid Howells 	struct afs_vlserver_list *vllist;
325359745d7SMuchun Song 	struct afs_cell *cell = pde_data(file_inode(m->file));
3261da177e4SLinus Torvalds 	loff_t pos = *_pos;
3271da177e4SLinus Torvalds 
3288b2a464cSDavid Howells 	rcu_read_lock();
3291da177e4SLinus Torvalds 
3300a5143f2SDavid Howells 	vllist = rcu_dereference(cell->vl_servers);
3310a5143f2SDavid Howells 	priv->vllist = vllist;
3321da177e4SLinus Torvalds 
3330a5143f2SDavid Howells 	if (pos < 0)
3340a5143f2SDavid Howells 		*_pos = pos = 0;
3350a5143f2SDavid Howells 	if (pos == 0)
3360a5143f2SDavid Howells 		return SEQ_START_TOKEN;
3371da177e4SLinus Torvalds 
338ca1cbbdcSDavid Howells 	if (pos - 1 >= vllist->nr_servers)
3391da177e4SLinus Torvalds 		return NULL;
3401da177e4SLinus Torvalds 
3410a5143f2SDavid Howells 	return &vllist->servers[pos - 1];
342ec26815aSDavid Howells }
3431da177e4SLinus Torvalds 
afs_proc_cell_vlservers_next(struct seq_file * m,void * v,loff_t * _pos)3445b86d4ffSDavid Howells static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
3451da177e4SLinus Torvalds 					  loff_t *_pos)
3461da177e4SLinus Torvalds {
3470a5143f2SDavid Howells 	struct afs_vl_seq_net_private *priv = m->private;
3480a5143f2SDavid Howells 	struct afs_vlserver_list *vllist = priv->vllist;
3491da177e4SLinus Torvalds 	loff_t pos;
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 	pos = *_pos;
3520a5143f2SDavid Howells 	pos++;
3530a5143f2SDavid Howells 	*_pos = pos;
3540a5143f2SDavid Howells 	if (!vllist || pos - 1 >= vllist->nr_servers)
3551da177e4SLinus Torvalds 		return NULL;
3561da177e4SLinus Torvalds 
3570a5143f2SDavid Howells 	return &vllist->servers[pos - 1];
358ec26815aSDavid Howells }
3591da177e4SLinus Torvalds 
afs_proc_cell_vlservers_stop(struct seq_file * m,void * v)3605b86d4ffSDavid Howells static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
361fe342cf7SDavid Howells 	__releases(rcu)
3621da177e4SLinus Torvalds {
3638b2a464cSDavid Howells 	rcu_read_unlock();
364ec26815aSDavid Howells }
3651da177e4SLinus Torvalds 
3665d9de25dSDavid Howells static const struct seq_operations afs_proc_cell_vlservers_ops = {
3675d9de25dSDavid Howells 	.start	= afs_proc_cell_vlservers_start,
3685d9de25dSDavid Howells 	.next	= afs_proc_cell_vlservers_next,
3695d9de25dSDavid Howells 	.stop	= afs_proc_cell_vlservers_stop,
3705d9de25dSDavid Howells 	.show	= afs_proc_cell_vlservers_show,
3715d9de25dSDavid Howells };
3725d9de25dSDavid Howells 
3731da177e4SLinus Torvalds /*
3745d9de25dSDavid Howells  * Display the list of fileservers we're using within a namespace.
3751da177e4SLinus Torvalds  */
afs_proc_servers_show(struct seq_file * m,void * v)376f0691689SDavid Howells static int afs_proc_servers_show(struct seq_file *m, void *v)
3771da177e4SLinus Torvalds {
378f0691689SDavid Howells 	struct afs_server *server;
379f0691689SDavid Howells 	struct afs_addr_list *alist;
3800aac4bceSDavid Howells 	int i;
3811da177e4SLinus Torvalds 
382f0691689SDavid Howells 	if (v == SEQ_START_TOKEN) {
3836d043a57SDavid Howells 		seq_puts(m, "UUID                                 REF ACT\n");
3841da177e4SLinus Torvalds 		return 0;
3851da177e4SLinus Torvalds 	}
3861da177e4SLinus Torvalds 
387f0691689SDavid Howells 	server = list_entry(v, struct afs_server, proc_link);
388f0691689SDavid Howells 	alist = rcu_dereference(server->addresses);
3896d043a57SDavid Howells 	seq_printf(m, "%pU %3d %3d\n",
390f0691689SDavid Howells 		   &server->uuid,
391*c56f9ec8SDavid Howells 		   refcount_read(&server->ref),
3926d043a57SDavid Howells 		   atomic_read(&server->active));
39332275d3fSDavid Howells 	seq_printf(m, "  - info: fl=%lx rtt=%u brk=%x\n",
39432275d3fSDavid Howells 		   server->flags, server->rtt, server->cb_s_break);
39532275d3fSDavid Howells 	seq_printf(m, "  - probe: last=%d out=%d\n",
39632275d3fSDavid Howells 		   (int)(jiffies - server->probed_at) / HZ,
39732275d3fSDavid Howells 		   atomic_read(&server->probe_outstanding));
39832275d3fSDavid Howells 	seq_printf(m, "  - ALIST v=%u rsp=%lx f=%lx\n",
39932275d3fSDavid Howells 		   alist->version, alist->responded, alist->failed);
4006d043a57SDavid Howells 	for (i = 0; i < alist->nr_addrs; i++)
4016d043a57SDavid Howells 		seq_printf(m, "    [%x] %pISpc%s\n",
4026d043a57SDavid Howells 			   i, &alist->addrs[i].transport,
4033bf0fb6fSDavid Howells 			   alist->preferred == i ? "*" : "");
4041da177e4SLinus Torvalds 	return 0;
405ec26815aSDavid Howells }
4061da177e4SLinus Torvalds 
afs_proc_servers_start(struct seq_file * m,loff_t * _pos)407d2ddc776SDavid Howells static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
408fe342cf7SDavid Howells 	__acquires(rcu)
4091da177e4SLinus Torvalds {
410d2ddc776SDavid Howells 	rcu_read_lock();
4115d9de25dSDavid Howells 	return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
412ec26815aSDavid Howells }
4131da177e4SLinus Torvalds 
afs_proc_servers_next(struct seq_file * m,void * v,loff_t * _pos)414d2ddc776SDavid Howells static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
4151da177e4SLinus Torvalds {
4165d9de25dSDavid Howells 	return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
417ec26815aSDavid Howells }
4181da177e4SLinus Torvalds 
afs_proc_servers_stop(struct seq_file * m,void * v)4195b86d4ffSDavid Howells static void afs_proc_servers_stop(struct seq_file *m, void *v)
420fe342cf7SDavid Howells 	__releases(rcu)
4211da177e4SLinus Torvalds {
422d2ddc776SDavid Howells 	rcu_read_unlock();
423ec26815aSDavid Howells }
4241da177e4SLinus Torvalds 
4255d9de25dSDavid Howells static const struct seq_operations afs_proc_servers_ops = {
4265d9de25dSDavid Howells 	.start	= afs_proc_servers_start,
4275d9de25dSDavid Howells 	.next	= afs_proc_servers_next,
4285d9de25dSDavid Howells 	.stop	= afs_proc_servers_stop,
4295d9de25dSDavid Howells 	.show	= afs_proc_servers_show,
4305d9de25dSDavid Howells };
4316f8880d8SDavid Howells 
4326f8880d8SDavid Howells /*
4335d9de25dSDavid Howells  * Display the list of strings that may be substituted for the @sys pathname
4345d9de25dSDavid Howells  * macro.
4356f8880d8SDavid Howells  */
afs_proc_sysname_show(struct seq_file * m,void * v)4365d9de25dSDavid Howells static int afs_proc_sysname_show(struct seq_file *m, void *v)
4376f8880d8SDavid Howells {
4385d9de25dSDavid Howells 	struct afs_net *net = afs_seq2net(m);
4395d9de25dSDavid Howells 	struct afs_sysnames *sysnames = net->sysnames;
4405d9de25dSDavid Howells 	unsigned int i = (unsigned long)v - 1;
4416f8880d8SDavid Howells 
4425d9de25dSDavid Howells 	if (i < sysnames->nr)
4435d9de25dSDavid Howells 		seq_printf(m, "%s\n", sysnames->subs[i]);
4446f8880d8SDavid Howells 	return 0;
4456f8880d8SDavid Howells }
4466f8880d8SDavid Howells 
afs_proc_sysname_start(struct seq_file * m,loff_t * pos)4475d9de25dSDavid Howells static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
4485d9de25dSDavid Howells 	__acquires(&net->sysnames_lock)
4495d9de25dSDavid Howells {
4505d9de25dSDavid Howells 	struct afs_net *net = afs_seq2net(m);
4515b86d4ffSDavid Howells 	struct afs_sysnames *names;
4525d9de25dSDavid Howells 
4535d9de25dSDavid Howells 	read_lock(&net->sysnames_lock);
4545d9de25dSDavid Howells 
4555b86d4ffSDavid Howells 	names = net->sysnames;
4565d9de25dSDavid Howells 	if (*pos >= names->nr)
4575d9de25dSDavid Howells 		return NULL;
4585d9de25dSDavid Howells 	return (void *)(unsigned long)(*pos + 1);
4595d9de25dSDavid Howells }
4605d9de25dSDavid Howells 
afs_proc_sysname_next(struct seq_file * m,void * v,loff_t * pos)4615d9de25dSDavid Howells static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
4625d9de25dSDavid Howells {
4635d9de25dSDavid Howells 	struct afs_net *net = afs_seq2net(m);
4645d9de25dSDavid Howells 	struct afs_sysnames *names = net->sysnames;
4655d9de25dSDavid Howells 
4665d9de25dSDavid Howells 	*pos += 1;
4675d9de25dSDavid Howells 	if (*pos >= names->nr)
4685d9de25dSDavid Howells 		return NULL;
4695d9de25dSDavid Howells 	return (void *)(unsigned long)(*pos + 1);
4705d9de25dSDavid Howells }
4715d9de25dSDavid Howells 
afs_proc_sysname_stop(struct seq_file * m,void * v)4725d9de25dSDavid Howells static void afs_proc_sysname_stop(struct seq_file *m, void *v)
4735d9de25dSDavid Howells 	__releases(&net->sysnames_lock)
4745d9de25dSDavid Howells {
4755d9de25dSDavid Howells 	struct afs_net *net = afs_seq2net(m);
4765d9de25dSDavid Howells 
4775d9de25dSDavid Howells 	read_unlock(&net->sysnames_lock);
4785d9de25dSDavid Howells }
4795d9de25dSDavid Howells 
4805d9de25dSDavid Howells static const struct seq_operations afs_proc_sysname_ops = {
4815d9de25dSDavid Howells 	.start	= afs_proc_sysname_start,
4825d9de25dSDavid Howells 	.next	= afs_proc_sysname_next,
4835d9de25dSDavid Howells 	.stop	= afs_proc_sysname_stop,
4845d9de25dSDavid Howells 	.show	= afs_proc_sysname_show,
4855d9de25dSDavid Howells };
4865d9de25dSDavid Howells 
4876f8880d8SDavid Howells /*
4885d9de25dSDavid Howells  * Allow the @sys substitution to be configured.
4896f8880d8SDavid Howells  */
afs_proc_sysname_write(struct file * file,char * buf,size_t size)4905b86d4ffSDavid Howells static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
4916f8880d8SDavid Howells {
4925b86d4ffSDavid Howells 	struct afs_sysnames *sysnames, *kill;
4936f8880d8SDavid Howells 	struct seq_file *m = file->private_data;
4945b86d4ffSDavid Howells 	struct afs_net *net = afs_seq2net(m);
4955b86d4ffSDavid Howells 	char *s, *p, *sub;
4966f8880d8SDavid Howells 	int ret, len;
4976f8880d8SDavid Howells 
4985b86d4ffSDavid Howells 	sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
4996f8880d8SDavid Howells 	if (!sysnames)
5005b86d4ffSDavid Howells 		return -ENOMEM;
5015b86d4ffSDavid Howells 	refcount_set(&sysnames->usage, 1);
5025b86d4ffSDavid Howells 	kill = sysnames;
5036f8880d8SDavid Howells 
5045b86d4ffSDavid Howells 	p = buf;
5056f8880d8SDavid Howells 	while ((s = strsep(&p, " \t\n"))) {
5066f8880d8SDavid Howells 		len = strlen(s);
5076f8880d8SDavid Howells 		if (len == 0)
5086f8880d8SDavid Howells 			continue;
5096f8880d8SDavid Howells 		ret = -ENAMETOOLONG;
5106f8880d8SDavid Howells 		if (len >= AFSNAMEMAX)
5116f8880d8SDavid Howells 			goto error;
5126f8880d8SDavid Howells 
5136f8880d8SDavid Howells 		if (len >= 4 &&
5146f8880d8SDavid Howells 		    s[len - 4] == '@' &&
5156f8880d8SDavid Howells 		    s[len - 3] == 's' &&
5166f8880d8SDavid Howells 		    s[len - 2] == 'y' &&
5176f8880d8SDavid Howells 		    s[len - 1] == 's')
5186f8880d8SDavid Howells 			/* Protect against recursion */
5196f8880d8SDavid Howells 			goto invalid;
5206f8880d8SDavid Howells 
5216f8880d8SDavid Howells 		if (s[0] == '.' &&
5226f8880d8SDavid Howells 		    (len < 2 || (len == 2 && s[1] == '.')))
5236f8880d8SDavid Howells 			goto invalid;
5246f8880d8SDavid Howells 
5256f8880d8SDavid Howells 		if (memchr(s, '/', len))
5266f8880d8SDavid Howells 			goto invalid;
5276f8880d8SDavid Howells 
5286f8880d8SDavid Howells 		ret = -EFBIG;
5296f8880d8SDavid Howells 		if (sysnames->nr >= AFS_NR_SYSNAME)
5306f8880d8SDavid Howells 			goto out;
5316f8880d8SDavid Howells 
5326f8880d8SDavid Howells 		if (strcmp(s, afs_init_sysname) == 0) {
5336f8880d8SDavid Howells 			sub = (char *)afs_init_sysname;
5346f8880d8SDavid Howells 		} else {
5356f8880d8SDavid Howells 			ret = -ENOMEM;
5366f8880d8SDavid Howells 			sub = kmemdup(s, len + 1, GFP_KERNEL);
5376f8880d8SDavid Howells 			if (!sub)
5386f8880d8SDavid Howells 				goto out;
5396f8880d8SDavid Howells 		}
5406f8880d8SDavid Howells 
5416f8880d8SDavid Howells 		sysnames->subs[sysnames->nr] = sub;
5426f8880d8SDavid Howells 		sysnames->nr++;
5436f8880d8SDavid Howells 	}
5446f8880d8SDavid Howells 
5455b86d4ffSDavid Howells 	if (sysnames->nr == 0) {
5465b86d4ffSDavid Howells 		sysnames->subs[0] = sysnames->blank;
5475b86d4ffSDavid Howells 		sysnames->nr++;
5485b86d4ffSDavid Howells 	}
5495b86d4ffSDavid Howells 
5505b86d4ffSDavid Howells 	write_lock(&net->sysnames_lock);
5515b86d4ffSDavid Howells 	kill = net->sysnames;
5525b86d4ffSDavid Howells 	net->sysnames = sysnames;
5535b86d4ffSDavid Howells 	write_unlock(&net->sysnames_lock);
5545b86d4ffSDavid Howells 	ret = 0;
5556f8880d8SDavid Howells out:
5565b86d4ffSDavid Howells 	afs_put_sysnames(kill);
5576f8880d8SDavid Howells 	return ret;
5586f8880d8SDavid Howells 
5596f8880d8SDavid Howells invalid:
5606f8880d8SDavid Howells 	ret = -EINVAL;
5616f8880d8SDavid Howells error:
5626f8880d8SDavid Howells 	goto out;
5636f8880d8SDavid Howells }
5646f8880d8SDavid Howells 
afs_put_sysnames(struct afs_sysnames * sysnames)5655d9de25dSDavid Howells void afs_put_sysnames(struct afs_sysnames *sysnames)
5665d9de25dSDavid Howells {
5675d9de25dSDavid Howells 	int i;
5685d9de25dSDavid Howells 
5695d9de25dSDavid Howells 	if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
5705d9de25dSDavid Howells 		for (i = 0; i < sysnames->nr; i++)
5715d9de25dSDavid Howells 			if (sysnames->subs[i] != afs_init_sysname &&
5725d9de25dSDavid Howells 			    sysnames->subs[i] != sysnames->blank)
5735d9de25dSDavid Howells 				kfree(sysnames->subs[i]);
5742ca068beSZhihao Cheng 		kfree(sysnames);
5755d9de25dSDavid Howells 	}
5765d9de25dSDavid Howells }
5775d9de25dSDavid Howells 
578d55b4da4SDavid Howells /*
579d55b4da4SDavid Howells  * Display general per-net namespace statistics
580d55b4da4SDavid Howells  */
afs_proc_stats_show(struct seq_file * m,void * v)581d55b4da4SDavid Howells static int afs_proc_stats_show(struct seq_file *m, void *v)
582d55b4da4SDavid Howells {
5835b86d4ffSDavid Howells 	struct afs_net *net = afs_seq2net_single(m);
584d55b4da4SDavid Howells 
585d55b4da4SDavid Howells 	seq_puts(m, "kAFS statistics\n");
586d55b4da4SDavid Howells 
587f3ddee8dSDavid Howells 	seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
588d55b4da4SDavid Howells 		   atomic_read(&net->n_lookup),
589d55b4da4SDavid Howells 		   atomic_read(&net->n_reval),
590f3ddee8dSDavid Howells 		   atomic_read(&net->n_inval),
591f3ddee8dSDavid Howells 		   atomic_read(&net->n_relpg));
592d55b4da4SDavid Howells 
593d55b4da4SDavid Howells 	seq_printf(m, "dir-data: rdpg=%u\n",
594d55b4da4SDavid Howells 		   atomic_read(&net->n_read_dir));
59563a4681fSDavid Howells 
59663a4681fSDavid Howells 	seq_printf(m, "dir-edit: cr=%u rm=%u\n",
59763a4681fSDavid Howells 		   atomic_read(&net->n_dir_cr),
59863a4681fSDavid Howells 		   atomic_read(&net->n_dir_rm));
59976a5cb6fSDavid Howells 
60076a5cb6fSDavid Howells 	seq_printf(m, "file-rd : n=%u nb=%lu\n",
60176a5cb6fSDavid Howells 		   atomic_read(&net->n_fetches),
60276a5cb6fSDavid Howells 		   atomic_long_read(&net->n_fetch_bytes));
60376a5cb6fSDavid Howells 	seq_printf(m, "file-wr : n=%u nb=%lu\n",
60476a5cb6fSDavid Howells 		   atomic_read(&net->n_stores),
60576a5cb6fSDavid Howells 		   atomic_long_read(&net->n_store_bytes));
606d55b4da4SDavid Howells 	return 0;
607d55b4da4SDavid Howells }
60810495a00SDavid Howells 
60910495a00SDavid Howells /*
61010495a00SDavid Howells  * initialise /proc/fs/afs/<cell>/
61110495a00SDavid Howells  */
afs_proc_cell_setup(struct afs_cell * cell)6125b86d4ffSDavid Howells int afs_proc_cell_setup(struct afs_cell *cell)
61310495a00SDavid Howells {
61410495a00SDavid Howells 	struct proc_dir_entry *dir;
6155b86d4ffSDavid Howells 	struct afs_net *net = cell->net;
61610495a00SDavid Howells 
61710495a00SDavid Howells 	_enter("%p{%s},%p", cell, cell->name, net->proc_afs);
61810495a00SDavid Howells 
6195b86d4ffSDavid Howells 	dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
62010495a00SDavid Howells 	if (!dir)
62110495a00SDavid Howells 		goto error_dir;
62210495a00SDavid Howells 
6235b86d4ffSDavid Howells 	if (!proc_create_net_data("vlservers", 0444, dir,
6245b86d4ffSDavid Howells 				  &afs_proc_cell_vlservers_ops,
6250a5143f2SDavid Howells 				  sizeof(struct afs_vl_seq_net_private),
6265b86d4ffSDavid Howells 				  cell) ||
6275b86d4ffSDavid Howells 	    !proc_create_net_data("volumes", 0444, dir,
6285b86d4ffSDavid Howells 				  &afs_proc_cell_volumes_ops,
6295b86d4ffSDavid Howells 				  sizeof(struct seq_net_private),
6305b86d4ffSDavid Howells 				  cell))
63110495a00SDavid Howells 		goto error_tree;
63210495a00SDavid Howells 
63310495a00SDavid Howells 	_leave(" = 0");
63410495a00SDavid Howells 	return 0;
63510495a00SDavid Howells 
63610495a00SDavid Howells error_tree:
63710495a00SDavid Howells 	remove_proc_subtree(cell->name, net->proc_afs);
63810495a00SDavid Howells error_dir:
63910495a00SDavid Howells 	_leave(" = -ENOMEM");
64010495a00SDavid Howells 	return -ENOMEM;
64110495a00SDavid Howells }
64210495a00SDavid Howells 
64310495a00SDavid Howells /*
64410495a00SDavid Howells  * remove /proc/fs/afs/<cell>/
64510495a00SDavid Howells  */
afs_proc_cell_remove(struct afs_cell * cell)6465b86d4ffSDavid Howells void afs_proc_cell_remove(struct afs_cell *cell)
64710495a00SDavid Howells {
6485b86d4ffSDavid Howells 	struct afs_net *net = cell->net;
6495b86d4ffSDavid Howells 
65010495a00SDavid Howells 	_enter("");
65110495a00SDavid Howells 	remove_proc_subtree(cell->name, net->proc_afs);
65210495a00SDavid Howells 	_leave("");
65310495a00SDavid Howells }
65410495a00SDavid Howells 
65510495a00SDavid Howells /*
65610495a00SDavid Howells  * initialise the /proc/fs/afs/ directory
65710495a00SDavid Howells  */
afs_proc_init(struct afs_net * net)65810495a00SDavid Howells int afs_proc_init(struct afs_net *net)
65910495a00SDavid Howells {
6605b86d4ffSDavid Howells 	struct proc_dir_entry *p;
6615b86d4ffSDavid Howells 
66210495a00SDavid Howells 	_enter("");
66310495a00SDavid Howells 
6645b86d4ffSDavid Howells 	p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
6655b86d4ffSDavid Howells 	if (!p)
66610495a00SDavid Howells 		goto error_dir;
66710495a00SDavid Howells 
6685b86d4ffSDavid Howells 	if (!proc_create_net_data_write("cells", 0644, p,
6695b86d4ffSDavid Howells 					&afs_proc_cells_ops,
6705b86d4ffSDavid Howells 					afs_proc_cells_write,
6715b86d4ffSDavid Howells 					sizeof(struct seq_net_private),
6725b86d4ffSDavid Howells 					NULL) ||
6735b86d4ffSDavid Howells 	    !proc_create_net_single_write("rootcell", 0644, p,
6745b86d4ffSDavid Howells 					  afs_proc_rootcell_show,
6755b86d4ffSDavid Howells 					  afs_proc_rootcell_write,
6765b86d4ffSDavid Howells 					  NULL) ||
6775b86d4ffSDavid Howells 	    !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
6785b86d4ffSDavid Howells 			     sizeof(struct seq_net_private)) ||
6795b86d4ffSDavid Howells 	    !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
6805b86d4ffSDavid Howells 	    !proc_create_net_data_write("sysname", 0644, p,
6815b86d4ffSDavid Howells 					&afs_proc_sysname_ops,
6825b86d4ffSDavid Howells 					afs_proc_sysname_write,
6835b86d4ffSDavid Howells 					sizeof(struct seq_net_private),
6845b86d4ffSDavid Howells 					NULL))
68510495a00SDavid Howells 		goto error_tree;
68610495a00SDavid Howells 
6875b86d4ffSDavid Howells 	net->proc_afs = p;
68810495a00SDavid Howells 	_leave(" = 0");
68910495a00SDavid Howells 	return 0;
69010495a00SDavid Howells 
69110495a00SDavid Howells error_tree:
6925b86d4ffSDavid Howells 	proc_remove(p);
69310495a00SDavid Howells error_dir:
69410495a00SDavid Howells 	_leave(" = -ENOMEM");
69510495a00SDavid Howells 	return -ENOMEM;
69610495a00SDavid Howells }
69710495a00SDavid Howells 
69810495a00SDavid Howells /*
69910495a00SDavid Howells  * clean up the /proc/fs/afs/ directory
70010495a00SDavid Howells  */
afs_proc_cleanup(struct afs_net * net)70110495a00SDavid Howells void afs_proc_cleanup(struct afs_net *net)
70210495a00SDavid Howells {
70310495a00SDavid Howells 	proc_remove(net->proc_afs);
70410495a00SDavid Howells 	net->proc_afs = NULL;
70510495a00SDavid Howells }
706