This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / fs / intermezzo / sysctl.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 1999 Peter J. Braam <braam@clusterfs.com>
5  *
6  *   This file is part of InterMezzo, http://www.inter-mezzo.org.
7  *
8  *   InterMezzo is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   InterMezzo is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with InterMezzo; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  *  Sysctrl entries for Intermezzo!
22  */
23
24 #include <linux/config.h> /* for CONFIG_PROC_FS */
25 #include <linux/module.h>
26 #include <linux/sched.h>
27 #include <linux/mm.h>
28 #include <linux/sysctl.h>
29 #include <linux/proc_fs.h>
30 #include <linux/slab.h>
31 #include <linux/vmalloc.h>
32 #include <linux/stat.h>
33 #include <linux/ctype.h>
34 #include <linux/init.h>
35 #include <asm/bitops.h>
36 #include <asm/segment.h>
37 #include <asm/uaccess.h>
38 #include <linux/utsname.h>
39
40 #include "intermezzo_fs.h"
41 #include "intermezzo_psdev.h"
42
43 /* /proc entries */
44
45 #ifdef CONFIG_PROC_FS
46 struct proc_dir_entry *proc_fs_intermezzo;
47 int intermezzo_mount_get_info( char * buffer, char ** start, off_t offset,
48                                int length)
49 {
50         int len=0;
51
52         /* this works as long as we are below 1024 characters! */
53         *start = buffer + offset;
54         len -= offset;
55
56         if ( len < 0 )
57                 return -EINVAL;
58
59         return len;
60 }
61
62 #endif
63
64
65 /* SYSCTL below */
66
67 static struct ctl_table_header *intermezzo_table_header = NULL;
68 /* 0x100 to avoid any chance of collisions at any point in the tree with
69  * non-directories
70  */
71 #define PSDEV_INTERMEZZO  (0x100)
72
73 #define PSDEV_DEBUG        1      /* control debugging */
74 #define PSDEV_TRACE        2      /* control enter/leave pattern */
75 #define PSDEV_TIMEOUT      3      /* timeout on upcalls to become intrble */
76 #define PSDEV_HARD         4      /* mount type "hard" or "soft" */
77 #define PSDEV_NO_FILTER    5      /* controls presto_chk */
78 #define PSDEV_NO_JOURNAL   6      /* controls presto_chk */
79 #define PSDEV_NO_UPCALL    7      /* controls lento_upcall */
80 #define PSDEV_ERRORVAL     8      /* controls presto_debug_fail_blkdev */
81 #define PSDEV_EXCL_GID     9      /* which GID is ignored by presto */
82 #define PSDEV_BYTES_TO_CLOSE 11   /* bytes to write before close */
83
84 /* These are global presto control options */
85 #define PRESTO_PRIMARY_CTLCNT 2
86 static struct ctl_table presto_table[ PRESTO_PRIMARY_CTLCNT + MAX_CHANNEL + 1] =
87 {
88         {PSDEV_DEBUG, "debug", &presto_debug, sizeof(int), 0644, NULL, &proc_dointvec},
89         {PSDEV_TRACE, "trace", &presto_print_entry, sizeof(int), 0644, NULL, &proc_dointvec},
90 };
91
92 /*
93  * Intalling the sysctl entries: strategy
94  * - have templates for each /proc/sys/intermezzo/ entry
95  *   such an entry exists for each /dev/presto
96  *    (proto_channel_entry)
97  * - have a template for the contents of such directories
98  *    (proto_psdev_table)
99  * - have the master table (presto_table)
100  *
101  * When installing, malloc, memcpy and fix up the pointers to point to
102  * the appropriate constants in izo_channels[your_minor]
103  */
104
105 static ctl_table proto_psdev_table[] = {
106         {PSDEV_HARD, "hard", 0, sizeof(int), 0644, NULL, &proc_dointvec},
107         {PSDEV_NO_FILTER, "no_filter", 0, sizeof(int), 0644, NULL, &proc_dointvec},
108         {PSDEV_NO_JOURNAL, "no_journal", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
109         {PSDEV_NO_UPCALL, "no_upcall", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
110         {PSDEV_TIMEOUT, "timeout", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
111 #ifdef PRESTO_DEBUG
112         {PSDEV_ERRORVAL, "errorval", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
113 #endif
114         { 0 }
115 };
116
117 static ctl_table proto_channel_entry = {
118         PSDEV_INTERMEZZO, 0,  NULL, 0, 0555, 0,
119 };
120
121 static ctl_table intermezzo_table[2] = {
122         {PSDEV_INTERMEZZO, "intermezzo",    NULL, 0, 0555, presto_table},
123         {0}
124 };
125
126 /* support for external setting and getting of opts. */
127 /* particularly via ioctl. The Right way to do this is via sysctl,
128  * but that will have to wait until intermezzo gets its own nice set of
129  * sysctl IDs
130  */
131 /* we made these separate as setting may in future be more restricted
132  * than getting
133  */
134 #ifdef RON_MINNICH
135 int dosetopt(int minor, struct psdev_opt *opt)
136 {
137         int retval = 0;
138         int newval = opt->optval;
139
140         ENTRY;
141
142         switch(opt->optname) {
143
144         case PSDEV_TIMEOUT:
145                 izo_channels[minor].uc_timeout = newval;
146                 break;
147
148         case PSDEV_HARD:
149                 izo_channels[minor].uc_hard = newval;
150                 break;
151
152         case PSDEV_NO_FILTER:
153                 izo_channels[minor].uc_no_filter = newval;
154                 break;
155
156         case PSDEV_NO_JOURNAL:
157                 izo_channels[minor].uc_no_journal = newval;
158                 break;
159
160         case PSDEV_NO_UPCALL:
161                 izo_channels[minor].uc_no_upcall = newval;
162                 break;
163
164 #ifdef PRESTO_DEBUG
165         case PSDEV_ERRORVAL: {
166                 /* If we have a positive arg, set a breakpoint for that
167                  * value.  If we have a negative arg, make that device
168                  * read-only.  FIXME  It would be much better to only
169                  * allow setting the underlying device read-only for the
170                  * current presto cache.
171                  */
172                 int errorval = izo_channels[minor].uc_errorval;
173                 if (errorval < 0) {
174                         if (newval == 0)
175                                 set_device_ro(-errorval, 0);
176                         else
177                                 CERROR("device %s already read only\n",
178                                        kdevname(-errorval));
179                 } else {
180                         if (newval < 0)
181                                 set_device_ro(-newval, 1);
182                         izo_channels[minor].uc_errorval = newval;
183                         CDEBUG(D_PSDEV, "setting errorval to %d\n", newval);
184                 }
185
186                 break;
187         }
188 #endif
189
190         case PSDEV_TRACE:
191         case PSDEV_DEBUG:
192         case PSDEV_BYTES_TO_CLOSE:
193         default:
194                 CDEBUG(D_PSDEV,
195                        "ioctl: dosetopt: minor %d, bad optname 0x%x, \n",
196                        minor, opt->optname);
197
198                 retval = -EINVAL;
199         }
200
201         EXIT;
202         return retval;
203 }
204
205 int dogetopt(int minor, struct psdev_opt *opt)
206 {
207         int retval = 0;
208
209         ENTRY;
210
211         switch(opt->optname) {
212
213         case PSDEV_TIMEOUT:
214                 opt->optval = izo_channels[minor].uc_timeout;
215                 break;
216
217         case PSDEV_HARD:
218                 opt->optval = izo_channels[minor].uc_hard;
219                 break;
220
221         case PSDEV_NO_FILTER:
222                 opt->optval = izo_channels[minor].uc_no_filter;
223                 break;
224
225         case PSDEV_NO_JOURNAL:
226                 opt->optval = izo_channels[minor].uc_no_journal;
227                 break;
228
229         case PSDEV_NO_UPCALL:
230                 opt->optval = izo_channels[minor].uc_no_upcall;
231                 break;
232
233 #ifdef PSDEV_DEBUG
234         case PSDEV_ERRORVAL: {
235                 int errorval = izo_channels[minor].uc_errorval;
236                 if (errorval < 0 && is_read_only(-errorval))
237                         CERROR("device %s has been set read-only\n",
238                                kdevname(-errorval));
239                 opt->optval = izo_channels[minor].uc_errorval;
240                 break;
241         }
242 #endif
243
244         case PSDEV_TRACE:
245         case PSDEV_DEBUG:
246         case PSDEV_BYTES_TO_CLOSE:
247         default:
248                 CDEBUG(D_PSDEV,
249                        "ioctl: dogetopt: minor %d, bad optval 0x%x, \n",
250                        minor, opt->optname);
251
252                 retval = -EINVAL;
253         }
254
255         EXIT;
256         return retval;
257 }
258 #endif
259
260
261 /* allocate the tables for the presto devices. We need
262  * sizeof(proto_channel_table)/sizeof(proto_channel_table[0])
263  * entries for each dev
264  */
265 int /* __init */ init_intermezzo_sysctl(void)
266 {
267         int i;
268         int total_dev = MAX_CHANNEL;
269         int entries_per_dev = sizeof(proto_psdev_table) /
270                 sizeof(proto_psdev_table[0]);
271         int total_entries = entries_per_dev * total_dev;
272         ctl_table *dev_ctl_table;
273
274         PRESTO_ALLOC(dev_ctl_table, sizeof(ctl_table) * total_entries);
275
276         if (! dev_ctl_table) {
277                 CERROR("WARNING: presto couldn't allocate dev_ctl_table\n");
278                 EXIT;
279                 return -ENOMEM;
280         }
281
282         /* now fill in the entries ... we put the individual presto<x>
283          * entries at the end of the table, and the per-presto stuff
284          * starting at the front.  We assume that the compiler makes
285          * this code more efficient, but really, who cares ... it
286          * happens once per reboot.
287          */
288         for(i = 0; i < total_dev; i++) {
289                 void *p;
290
291                 /* entry for this /proc/sys/intermezzo/intermezzo"i" */
292                 ctl_table *psdev = &presto_table[i + PRESTO_PRIMARY_CTLCNT];
293                 /* entries for the individual "files" in this "directory" */
294                 ctl_table *psdev_entries = &dev_ctl_table[i * entries_per_dev];
295                 /* init the psdev and psdev_entries with the prototypes */
296                 *psdev = proto_channel_entry;
297                 memcpy(psdev_entries, proto_psdev_table,
298                        sizeof(proto_psdev_table));
299                 /* now specialize them ... */
300                 /* the psdev has to point to psdev_entries, and fix the number */
301                 psdev->ctl_name = psdev->ctl_name + i + 1; /* sorry */
302
303                 PRESTO_ALLOC(p, PROCNAME_SIZE);
304                 psdev->procname = p;
305                 if (!psdev->procname) {
306                         PRESTO_FREE(dev_ctl_table,
307                                     sizeof(ctl_table) * total_entries);
308                         return -ENOMEM;
309                 }
310                 sprintf((char *) psdev->procname, "intermezzo%d", i);
311                 /* hook presto into */
312                 psdev->child = psdev_entries;
313
314                 /* now for each psdev entry ... */
315                 psdev_entries[0].data = &(izo_channels[i].uc_hard);
316                 psdev_entries[1].data = &(izo_channels[i].uc_no_filter);
317                 psdev_entries[2].data = &(izo_channels[i].uc_no_journal);
318                 psdev_entries[3].data = &(izo_channels[i].uc_no_upcall);
319                 psdev_entries[4].data = &(izo_channels[i].uc_timeout);
320 #ifdef PRESTO_DEBUG
321                 psdev_entries[5].data = &(izo_channels[i].uc_errorval);
322 #endif
323         }
324
325
326 #ifdef CONFIG_SYSCTL
327         if ( !intermezzo_table_header )
328                 intermezzo_table_header =
329                         register_sysctl_table(intermezzo_table, 0);
330 #endif
331 #ifdef CONFIG_PROC_FS
332         proc_fs_intermezzo = proc_mkdir("intermezzo", proc_root_fs);
333         proc_fs_intermezzo->owner = THIS_MODULE;
334         create_proc_info_entry("mounts", 0, proc_fs_intermezzo, 
335                                intermezzo_mount_get_info);
336 #endif
337         return 0;
338 }
339
340 void cleanup_intermezzo_sysctl(void)
341 {
342         int total_dev = MAX_CHANNEL;
343         int entries_per_dev = sizeof(proto_psdev_table) /
344                 sizeof(proto_psdev_table[0]);
345         int total_entries = entries_per_dev * total_dev;
346         int i;
347
348 #ifdef CONFIG_SYSCTL
349         if ( intermezzo_table_header )
350                 unregister_sysctl_table(intermezzo_table_header);
351         intermezzo_table_header = NULL;
352 #endif
353         for(i = 0; i < total_dev; i++) {
354                 /* entry for this /proc/sys/intermezzo/intermezzo"i" */
355                 ctl_table *psdev = &presto_table[i + PRESTO_PRIMARY_CTLCNT];
356                 PRESTO_FREE(psdev->procname, PROCNAME_SIZE);
357         }
358         /* presto_table[PRESTO_PRIMARY_CTLCNT].child points to the
359          * dev_ctl_table previously allocated in init_intermezzo_psdev()
360          */
361         PRESTO_FREE(presto_table[PRESTO_PRIMARY_CTLCNT].child, sizeof(ctl_table) * total_entries);
362
363 #ifdef CONFIG_PROC_FS
364         remove_proc_entry("mounts", proc_fs_intermezzo);
365         remove_proc_entry("intermezzo", proc_root_fs);
366 #endif
367 }
368