afs: Implement VL server rotation
[linux-block.git] / fs / afs / proc.c
CommitLineData
ec26815a 1/* /proc interface for AFS
1da177e4
LT
2 *
3 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
1da177e4
LT
12#include <linux/slab.h>
13#include <linux/module.h>
14#include <linux/proc_fs.h>
15#include <linux/seq_file.h>
e8edc6e0 16#include <linux/sched.h>
7c0f6ba6 17#include <linux/uaccess.h>
1da177e4
LT
18#include "internal.h"
19
0a5143f2
DH
20struct afs_vl_seq_net_private {
21 struct seq_net_private seq; /* Must be first */
22 struct afs_vlserver_list *vllist;
23};
24
5b86d4ff 25static inline struct afs_net *afs_seq2net(struct seq_file *m)
f044c884 26{
5b86d4ff 27 return afs_net(seq_file_net(m));
f044c884 28}
1da177e4 29
5b86d4ff 30static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
f044c884 31{
5b86d4ff 32 return afs_net(seq_file_single_net(m));
f044c884 33}
1da177e4 34
f0691689 35/*
5d9de25d 36 * Display the list of cells known to the namespace.
f0691689
DH
37 */
38static int afs_proc_cells_show(struct seq_file *m, void *v)
39{
40 struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
f0691689 41
6b3944e4 42 if (v == SEQ_START_TOKEN) {
f0691689
DH
43 /* display header on line 1 */
44 seq_puts(m, "USE NAME\n");
45 return 0;
46 }
47
48 /* display one cell per line on subsequent lines */
49 seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
50 return 0;
51}
52
1da177e4 53static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
fe342cf7 54 __acquires(rcu)
1da177e4 55{
989782dc 56 rcu_read_lock();
6b3944e4 57 return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos);
ec26815a 58}
1da177e4 59
f044c884 60static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
1da177e4 61{
6b3944e4 62 return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos);
ec26815a 63}
1da177e4 64
f044c884 65static void afs_proc_cells_stop(struct seq_file *m, void *v)
fe342cf7 66 __releases(rcu)
1da177e4 67{
989782dc 68 rcu_read_unlock();
ec26815a 69}
1da177e4 70
5d9de25d
DH
71static const struct seq_operations afs_proc_cells_ops = {
72 .start = afs_proc_cells_start,
73 .next = afs_proc_cells_next,
74 .stop = afs_proc_cells_stop,
75 .show = afs_proc_cells_show,
76};
77
1da177e4
LT
78/*
79 * handle writes to /proc/fs/afs/cells
80 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
81 */
5b86d4ff 82static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
1da177e4 83{
5b86d4ff
DH
84 struct seq_file *m = file->private_data;
85 struct afs_net *net = afs_seq2net(m);
86 char *name, *args;
1da177e4
LT
87 int ret;
88
1da177e4 89 /* trim to first NL */
5b86d4ff 90 name = memchr(buf, '\n', size);
1da177e4
LT
91 if (name)
92 *name = 0;
93
94 /* split into command, name and argslist */
5b86d4ff 95 name = strchr(buf, ' ');
1da177e4
LT
96 if (!name)
97 goto inval;
98 do {
99 *name++ = 0;
100 } while(*name == ' ');
101 if (!*name)
102 goto inval;
103
104 args = strchr(name, ' ');
ecfe951f
DH
105 if (args) {
106 do {
107 *args++ = 0;
108 } while(*args == ' ');
109 if (!*args)
110 goto inval;
111 }
1da177e4
LT
112
113 /* determine command to perform */
5b86d4ff 114 _debug("cmd=%s name=%s args=%s", buf, name, args);
1da177e4 115
5b86d4ff 116 if (strcmp(buf, "add") == 0) {
1da177e4 117 struct afs_cell *cell;
08e0e7c8 118
989782dc 119 cell = afs_lookup_cell(net, name, strlen(name), args, true);
08e0e7c8
DH
120 if (IS_ERR(cell)) {
121 ret = PTR_ERR(cell);
1da177e4 122 goto done;
08e0e7c8 123 }
1da177e4 124
17814aef
DH
125 if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
126 afs_put_cell(net, cell);
ec26815a 127 } else {
1da177e4
LT
128 goto inval;
129 }
130
5b86d4ff 131 ret = 0;
1da177e4 132
ec26815a 133done:
1da177e4
LT
134 _leave(" = %d", ret);
135 return ret;
136
ec26815a 137inval:
1da177e4
LT
138 ret = -EINVAL;
139 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
140 goto done;
ec26815a 141}
1da177e4 142
5d9de25d 143/*
5b86d4ff 144 * Display the name of the current workstation cell.
5d9de25d 145 */
5b86d4ff 146static int afs_proc_rootcell_show(struct seq_file *m, void *v)
1da177e4 147{
37ab6368 148 struct afs_cell *cell;
5b86d4ff
DH
149 struct afs_net *net;
150
151 net = afs_seq2net_single(m);
152 if (rcu_access_pointer(net->ws_cell)) {
153 rcu_read_lock();
154 cell = rcu_dereference(net->ws_cell);
155 if (cell)
156 seq_printf(m, "%s\n", cell->name);
157 rcu_read_unlock();
158 }
159 return 0;
1da177e4
LT
160}
161
1da177e4 162/*
5d9de25d
DH
163 * Set the current workstation cell and optionally supply its list of volume
164 * location servers.
165 *
166 * echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
1da177e4 167 */
5b86d4ff 168static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
1da177e4 169{
5b86d4ff
DH
170 struct seq_file *m = file->private_data;
171 struct afs_net *net = afs_seq2net_single(m);
172 char *s;
1da177e4
LT
173 int ret;
174
6f8880d8 175 ret = -EINVAL;
5b86d4ff 176 if (buf[0] == '.')
6f8880d8 177 goto out;
5b86d4ff 178 if (memchr(buf, '/', size))
6f8880d8
DH
179 goto out;
180
1da177e4 181 /* trim to first NL */
5b86d4ff 182 s = memchr(buf, '\n', size);
1da177e4
LT
183 if (s)
184 *s = 0;
185
186 /* determine command to perform */
5b86d4ff 187 _debug("rootcell=%s", buf);
1da177e4 188
5b86d4ff 189 ret = afs_cell_init(net, buf);
1da177e4 190
6f8880d8 191out:
1da177e4
LT
192 _leave(" = %d", ret);
193 return ret;
ec26815a 194}
1da177e4 195
f0691689
DH
196static const char afs_vol_types[3][3] = {
197 [AFSVL_RWVOL] = "RW",
198 [AFSVL_ROVOL] = "RO",
199 [AFSVL_BACKVOL] = "BK",
200};
201
202/*
5d9de25d 203 * Display the list of volumes known to a cell.
f0691689
DH
204 */
205static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
206{
207 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
208 struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
209
210 /* Display header on line 1 */
211 if (v == &cell->proc_volumes) {
212 seq_puts(m, "USE VID TY\n");
213 return 0;
214 }
215
216 seq_printf(m, "%3d %08x %s\n",
217 atomic_read(&vol->usage), vol->vid,
218 afs_vol_types[vol->type]);
219
220 return 0;
221}
222
1da177e4 223static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
fe342cf7 224 __acquires(cell->proc_lock)
1da177e4 225{
353861cf 226 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
1da177e4 227
d2ddc776
DH
228 read_lock(&cell->proc_lock);
229 return seq_list_start_head(&cell->proc_volumes, *_pos);
ec26815a 230}
1da177e4 231
5b86d4ff 232static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
1da177e4
LT
233 loff_t *_pos)
234{
5b86d4ff 235 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
1da177e4 236
d2ddc776 237 return seq_list_next(v, &cell->proc_volumes, _pos);
ec26815a 238}
1da177e4 239
5b86d4ff 240static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
fe342cf7 241 __releases(cell->proc_lock)
1da177e4 242{
5b86d4ff 243 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
1da177e4 244
d2ddc776 245 read_unlock(&cell->proc_lock);
ec26815a 246}
1da177e4 247
5d9de25d
DH
248static const struct seq_operations afs_proc_cell_volumes_ops = {
249 .start = afs_proc_cell_volumes_start,
250 .next = afs_proc_cell_volumes_next,
251 .stop = afs_proc_cell_volumes_stop,
252 .show = afs_proc_cell_volumes_show,
253};
254
0a5143f2
DH
255static const char *const dns_record_sources[NR__dns_record_source + 1] = {
256 [DNS_RECORD_UNAVAILABLE] = "unav",
257 [DNS_RECORD_FROM_CONFIG] = "cfg",
258 [DNS_RECORD_FROM_DNS_A] = "A",
259 [DNS_RECORD_FROM_DNS_AFSDB] = "AFSDB",
260 [DNS_RECORD_FROM_DNS_SRV] = "SRV",
261 [DNS_RECORD_FROM_NSS] = "nss",
262 [NR__dns_record_source] = "[weird]"
263};
264
265static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = {
266 [DNS_LOOKUP_NOT_DONE] = "no-lookup",
267 [DNS_LOOKUP_GOOD] = "good",
268 [DNS_LOOKUP_GOOD_WITH_BAD] = "good/bad",
269 [DNS_LOOKUP_BAD] = "bad",
270 [DNS_LOOKUP_GOT_NOT_FOUND] = "not-found",
271 [DNS_LOOKUP_GOT_LOCAL_FAILURE] = "local-failure",
272 [DNS_LOOKUP_GOT_TEMP_FAILURE] = "temp-failure",
273 [DNS_LOOKUP_GOT_NS_FAILURE] = "ns-failure",
274 [NR__dns_lookup_status] = "[weird]"
275};
276
1da177e4 277/*
5d9de25d 278 * Display the list of Volume Location servers we're using for a cell.
1da177e4 279 */
f0691689 280static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
1da177e4 281{
0a5143f2
DH
282 const struct afs_vl_seq_net_private *priv = m->private;
283 const struct afs_vlserver_list *vllist = priv->vllist;
284 const struct afs_vlserver_entry *entry;
285 const struct afs_vlserver *vlserver;
286 const struct afs_addr_list *alist;
287 int i;
1da177e4 288
0a5143f2
DH
289 if (v == SEQ_START_TOKEN) {
290 seq_printf(m, "# source %s, status %s\n",
291 dns_record_sources[vllist->source],
292 dns_lookup_statuses[vllist->status]);
1da177e4
LT
293 return 0;
294 }
295
0a5143f2
DH
296 entry = v;
297 vlserver = entry->server;
298 alist = rcu_dereference(vlserver->addresses);
299
300 seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n",
301 vlserver->name, entry->priority, entry->weight,
302 dns_record_sources[alist ? alist->source : entry->source],
303 dns_lookup_statuses[alist ? alist->status : entry->status]);
304 if (alist) {
305 for (i = 0; i < alist->nr_addrs; i++)
306 seq_printf(m, " %c %pISpc\n",
307 alist->index == i ? '>' : '-',
308 &alist->addrs[i].transport);
309 }
1da177e4 310 return 0;
ec26815a 311}
1da177e4 312
1da177e4 313static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
fe342cf7 314 __acquires(rcu)
1da177e4 315{
0a5143f2
DH
316 struct afs_vl_seq_net_private *priv = m->private;
317 struct afs_vlserver_list *vllist;
353861cf 318 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
1da177e4
LT
319 loff_t pos = *_pos;
320
8b2a464c 321 rcu_read_lock();
1da177e4 322
0a5143f2
DH
323 vllist = rcu_dereference(cell->vl_servers);
324 priv->vllist = vllist;
1da177e4 325
0a5143f2
DH
326 if (pos < 0)
327 *_pos = pos = 0;
328 if (pos == 0)
329 return SEQ_START_TOKEN;
1da177e4 330
0a5143f2 331 if (!vllist || pos - 1 >= vllist->nr_servers)
1da177e4
LT
332 return NULL;
333
0a5143f2 334 return &vllist->servers[pos - 1];
ec26815a 335}
1da177e4 336
5b86d4ff 337static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
1da177e4
LT
338 loff_t *_pos)
339{
0a5143f2
DH
340 struct afs_vl_seq_net_private *priv = m->private;
341 struct afs_vlserver_list *vllist = priv->vllist;
1da177e4
LT
342 loff_t pos;
343
1da177e4 344 pos = *_pos;
0a5143f2
DH
345 pos++;
346 *_pos = pos;
347 if (!vllist || pos - 1 >= vllist->nr_servers)
1da177e4
LT
348 return NULL;
349
0a5143f2 350 return &vllist->servers[pos - 1];
ec26815a 351}
1da177e4 352
5b86d4ff 353static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
fe342cf7 354 __releases(rcu)
1da177e4 355{
8b2a464c 356 rcu_read_unlock();
ec26815a 357}
1da177e4 358
5d9de25d
DH
359static const struct seq_operations afs_proc_cell_vlservers_ops = {
360 .start = afs_proc_cell_vlservers_start,
361 .next = afs_proc_cell_vlservers_next,
362 .stop = afs_proc_cell_vlservers_stop,
363 .show = afs_proc_cell_vlservers_show,
364};
365
1da177e4 366/*
5d9de25d 367 * Display the list of fileservers we're using within a namespace.
1da177e4 368 */
f0691689 369static int afs_proc_servers_show(struct seq_file *m, void *v)
1da177e4 370{
f0691689
DH
371 struct afs_server *server;
372 struct afs_addr_list *alist;
0aac4bce 373 int i;
1da177e4 374
f0691689
DH
375 if (v == SEQ_START_TOKEN) {
376 seq_puts(m, "UUID USE ADDR\n");
1da177e4
LT
377 return 0;
378 }
379
f0691689
DH
380 server = list_entry(v, struct afs_server, proc_link);
381 alist = rcu_dereference(server->addresses);
0aac4bce 382 seq_printf(m, "%pU %3d %pISpc%s\n",
f0691689
DH
383 &server->uuid,
384 atomic_read(&server->usage),
0aac4bce
DH
385 &alist->addrs[0].transport,
386 alist->index == 0 ? "*" : "");
387 for (i = 1; i < alist->nr_addrs; i++)
388 seq_printf(m, " %pISpc%s\n",
389 &alist->addrs[i].transport,
390 alist->index == i ? "*" : "");
1da177e4 391 return 0;
ec26815a 392}
1da177e4 393
d2ddc776 394static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
fe342cf7 395 __acquires(rcu)
1da177e4 396{
d2ddc776 397 rcu_read_lock();
5d9de25d 398 return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
ec26815a 399}
1da177e4 400
d2ddc776 401static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
1da177e4 402{
5d9de25d 403 return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
ec26815a 404}
1da177e4 405
5b86d4ff 406static void afs_proc_servers_stop(struct seq_file *m, void *v)
fe342cf7 407 __releases(rcu)
1da177e4 408{
d2ddc776 409 rcu_read_unlock();
ec26815a 410}
1da177e4 411
5d9de25d
DH
412static const struct seq_operations afs_proc_servers_ops = {
413 .start = afs_proc_servers_start,
414 .next = afs_proc_servers_next,
415 .stop = afs_proc_servers_stop,
416 .show = afs_proc_servers_show,
417};
6f8880d8 418
6f8880d8 419/*
5d9de25d
DH
420 * Display the list of strings that may be substituted for the @sys pathname
421 * macro.
6f8880d8 422 */
5d9de25d 423static int afs_proc_sysname_show(struct seq_file *m, void *v)
6f8880d8 424{
5d9de25d
DH
425 struct afs_net *net = afs_seq2net(m);
426 struct afs_sysnames *sysnames = net->sysnames;
427 unsigned int i = (unsigned long)v - 1;
6f8880d8 428
5d9de25d
DH
429 if (i < sysnames->nr)
430 seq_printf(m, "%s\n", sysnames->subs[i]);
431 return 0;
432}
6f8880d8 433
5d9de25d
DH
434static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
435 __acquires(&net->sysnames_lock)
436{
437 struct afs_net *net = afs_seq2net(m);
5b86d4ff 438 struct afs_sysnames *names;
6f8880d8 439
5d9de25d 440 read_lock(&net->sysnames_lock);
6f8880d8 441
5b86d4ff 442 names = net->sysnames;
5d9de25d
DH
443 if (*pos >= names->nr)
444 return NULL;
445 return (void *)(unsigned long)(*pos + 1);
446}
447
448static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
449{
450 struct afs_net *net = afs_seq2net(m);
451 struct afs_sysnames *names = net->sysnames;
452
453 *pos += 1;
454 if (*pos >= names->nr)
455 return NULL;
456 return (void *)(unsigned long)(*pos + 1);
457}
458
459static void afs_proc_sysname_stop(struct seq_file *m, void *v)
460 __releases(&net->sysnames_lock)
461{
462 struct afs_net *net = afs_seq2net(m);
463
464 read_unlock(&net->sysnames_lock);
6f8880d8
DH
465}
466
5d9de25d
DH
467static const struct seq_operations afs_proc_sysname_ops = {
468 .start = afs_proc_sysname_start,
469 .next = afs_proc_sysname_next,
470 .stop = afs_proc_sysname_stop,
471 .show = afs_proc_sysname_show,
472};
473
6f8880d8 474/*
5d9de25d 475 * Allow the @sys substitution to be configured.
6f8880d8 476 */
5b86d4ff 477static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
6f8880d8 478{
5b86d4ff 479 struct afs_sysnames *sysnames, *kill;
6f8880d8 480 struct seq_file *m = file->private_data;
5b86d4ff
DH
481 struct afs_net *net = afs_seq2net(m);
482 char *s, *p, *sub;
6f8880d8
DH
483 int ret, len;
484
5b86d4ff 485 sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
6f8880d8 486 if (!sysnames)
5b86d4ff
DH
487 return -ENOMEM;
488 refcount_set(&sysnames->usage, 1);
489 kill = sysnames;
6f8880d8 490
5b86d4ff 491 p = buf;
6f8880d8
DH
492 while ((s = strsep(&p, " \t\n"))) {
493 len = strlen(s);
494 if (len == 0)
495 continue;
496 ret = -ENAMETOOLONG;
497 if (len >= AFSNAMEMAX)
498 goto error;
499
500 if (len >= 4 &&
501 s[len - 4] == '@' &&
502 s[len - 3] == 's' &&
503 s[len - 2] == 'y' &&
504 s[len - 1] == 's')
505 /* Protect against recursion */
506 goto invalid;
507
508 if (s[0] == '.' &&
509 (len < 2 || (len == 2 && s[1] == '.')))
510 goto invalid;
511
512 if (memchr(s, '/', len))
513 goto invalid;
514
515 ret = -EFBIG;
516 if (sysnames->nr >= AFS_NR_SYSNAME)
517 goto out;
518
519 if (strcmp(s, afs_init_sysname) == 0) {
520 sub = (char *)afs_init_sysname;
521 } else {
522 ret = -ENOMEM;
523 sub = kmemdup(s, len + 1, GFP_KERNEL);
524 if (!sub)
525 goto out;
526 }
527
528 sysnames->subs[sysnames->nr] = sub;
529 sysnames->nr++;
530 }
531
5b86d4ff
DH
532 if (sysnames->nr == 0) {
533 sysnames->subs[0] = sysnames->blank;
534 sysnames->nr++;
535 }
536
537 write_lock(&net->sysnames_lock);
538 kill = net->sysnames;
539 net->sysnames = sysnames;
540 write_unlock(&net->sysnames_lock);
541 ret = 0;
6f8880d8 542out:
5b86d4ff 543 afs_put_sysnames(kill);
6f8880d8
DH
544 return ret;
545
546invalid:
547 ret = -EINVAL;
548error:
6f8880d8
DH
549 goto out;
550}
551
5d9de25d
DH
552void afs_put_sysnames(struct afs_sysnames *sysnames)
553{
554 int i;
555
556 if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
557 for (i = 0; i < sysnames->nr; i++)
558 if (sysnames->subs[i] != afs_init_sysname &&
559 sysnames->subs[i] != sysnames->blank)
560 kfree(sysnames->subs[i]);
561 }
562}
563
d55b4da4
DH
564/*
565 * Display general per-net namespace statistics
566 */
567static int afs_proc_stats_show(struct seq_file *m, void *v)
568{
5b86d4ff 569 struct afs_net *net = afs_seq2net_single(m);
d55b4da4
DH
570
571 seq_puts(m, "kAFS statistics\n");
572
f3ddee8d 573 seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
d55b4da4
DH
574 atomic_read(&net->n_lookup),
575 atomic_read(&net->n_reval),
f3ddee8d
DH
576 atomic_read(&net->n_inval),
577 atomic_read(&net->n_relpg));
d55b4da4
DH
578
579 seq_printf(m, "dir-data: rdpg=%u\n",
580 atomic_read(&net->n_read_dir));
63a4681f
DH
581
582 seq_printf(m, "dir-edit: cr=%u rm=%u\n",
583 atomic_read(&net->n_dir_cr),
584 atomic_read(&net->n_dir_rm));
76a5cb6f
DH
585
586 seq_printf(m, "file-rd : n=%u nb=%lu\n",
587 atomic_read(&net->n_fetches),
588 atomic_long_read(&net->n_fetch_bytes));
589 seq_printf(m, "file-wr : n=%u nb=%lu\n",
590 atomic_read(&net->n_stores),
591 atomic_long_read(&net->n_store_bytes));
d55b4da4
DH
592 return 0;
593}
10495a00
DH
594
595/*
596 * initialise /proc/fs/afs/<cell>/
597 */
5b86d4ff 598int afs_proc_cell_setup(struct afs_cell *cell)
10495a00
DH
599{
600 struct proc_dir_entry *dir;
5b86d4ff 601 struct afs_net *net = cell->net;
10495a00
DH
602
603 _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
604
5b86d4ff 605 dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
10495a00
DH
606 if (!dir)
607 goto error_dir;
608
5b86d4ff
DH
609 if (!proc_create_net_data("vlservers", 0444, dir,
610 &afs_proc_cell_vlservers_ops,
0a5143f2 611 sizeof(struct afs_vl_seq_net_private),
5b86d4ff
DH
612 cell) ||
613 !proc_create_net_data("volumes", 0444, dir,
614 &afs_proc_cell_volumes_ops,
615 sizeof(struct seq_net_private),
616 cell))
10495a00
DH
617 goto error_tree;
618
619 _leave(" = 0");
620 return 0;
621
622error_tree:
623 remove_proc_subtree(cell->name, net->proc_afs);
624error_dir:
625 _leave(" = -ENOMEM");
626 return -ENOMEM;
627}
628
629/*
630 * remove /proc/fs/afs/<cell>/
631 */
5b86d4ff 632void afs_proc_cell_remove(struct afs_cell *cell)
10495a00 633{
5b86d4ff 634 struct afs_net *net = cell->net;
10495a00 635
5b86d4ff 636 _enter("");
10495a00 637 remove_proc_subtree(cell->name, net->proc_afs);
10495a00
DH
638 _leave("");
639}
640
641/*
642 * initialise the /proc/fs/afs/ directory
643 */
644int afs_proc_init(struct afs_net *net)
645{
5b86d4ff
DH
646 struct proc_dir_entry *p;
647
10495a00
DH
648 _enter("");
649
5b86d4ff
DH
650 p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
651 if (!p)
10495a00
DH
652 goto error_dir;
653
5b86d4ff
DH
654 if (!proc_create_net_data_write("cells", 0644, p,
655 &afs_proc_cells_ops,
656 afs_proc_cells_write,
657 sizeof(struct seq_net_private),
658 NULL) ||
659 !proc_create_net_single_write("rootcell", 0644, p,
660 afs_proc_rootcell_show,
661 afs_proc_rootcell_write,
662 NULL) ||
663 !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
664 sizeof(struct seq_net_private)) ||
665 !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
666 !proc_create_net_data_write("sysname", 0644, p,
667 &afs_proc_sysname_ops,
668 afs_proc_sysname_write,
669 sizeof(struct seq_net_private),
670 NULL))
10495a00
DH
671 goto error_tree;
672
5b86d4ff 673 net->proc_afs = p;
10495a00
DH
674 _leave(" = 0");
675 return 0;
676
677error_tree:
5b86d4ff 678 proc_remove(p);
10495a00
DH
679error_dir:
680 _leave(" = -ENOMEM");
681 return -ENOMEM;
682}
683
684/*
685 * clean up the /proc/fs/afs/ directory
686 */
687void afs_proc_cleanup(struct afs_net *net)
688{
689 proc_remove(net->proc_afs);
690 net->proc_afs = NULL;
691}