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