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;
intermezzo_mount_get_info(char * buffer,char ** start,off_t offset,int length)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
dosetopt(int minor,struct psdev_opt * opt)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
dogetopt(int minor,struct psdev_opt * opt)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 */
init_intermezzo_sysctl(void)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
cleanup_intermezzo_sysctl(void)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
373