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
50struct proc_dir_entry *proc_fs_intermezzo;
51int 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
71static 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
90static 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
109static 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
121static ctl_table proto_channel_entry = {
122	PSDEV_INTERMEZZO, 0,  NULL, 0, 0555, 0,
123};
124
125static 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
139int 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		int errorval = izo_channels[minor].uc_errorval;
171		if (errorval < 0) {
172			if (newval == 0)
173				set_device_ro(-errorval, 0);
174			else
175				CERROR("device %s already read only\n",
176				       kdevname(-errorval));
177		} else {
178			if (newval < 0)
179				set_device_ro(-newval, 1);
180			izo_channels[minor].uc_errorval = newval;
181			CDEBUG(D_PSDEV, "setting errorval to %d\n", newval);
182		}
183
184		break;
185	}
186#endif
187
188	case PSDEV_TRACE:
189	case PSDEV_DEBUG:
190	case PSDEV_BYTES_TO_CLOSE:
191	default:
192		CDEBUG(D_PSDEV,
193		       "ioctl: dosetopt: minor %d, bad optname 0x%x, \n",
194		       minor, opt->optname);
195
196		retval = -EINVAL;
197	}
198
199	EXIT;
200	return retval;
201}
202
203int dogetopt(int minor, struct psdev_opt *opt)
204{
205	int retval = 0;
206
207	ENTRY;
208
209	switch(opt->optname) {
210
211	case PSDEV_TIMEOUT:
212		opt->optval = izo_channels[minor].uc_timeout;
213		break;
214
215	case PSDEV_HARD:
216		opt->optval = izo_channels[minor].uc_hard;
217		break;
218
219	case PSDEV_NO_FILTER:
220		opt->optval = izo_channels[minor].uc_no_filter;
221		break;
222
223	case PSDEV_NO_JOURNAL:
224		opt->optval = izo_channels[minor].uc_no_journal;
225		break;
226
227	case PSDEV_NO_UPCALL:
228		opt->optval = izo_channels[minor].uc_no_upcall;
229		break;
230
231#ifdef PSDEV_DEBUG
232	case PSDEV_ERRORVAL: {
233		int errorval = izo_channels[minor].uc_errorval;
234		if (errorval < 0 && is_read_only(-errorval))
235			CERROR("device %s has been set read-only\n",
236			       kdevname(-errorval));
237		opt->optval = izo_channels[minor].uc_errorval;
238		break;
239	}
240#endif
241
242	case PSDEV_TRACE:
243	case PSDEV_DEBUG:
244	case PSDEV_BYTES_TO_CLOSE:
245	default:
246		CDEBUG(D_PSDEV,
247		       "ioctl: dogetopt: minor %d, bad optval 0x%x, \n",
248		       minor, opt->optname);
249
250		retval = -EINVAL;
251	}
252
253	EXIT;
254	return retval;
255}
256#endif
257
258
259/* allocate the tables for the presto devices. We need
260 * sizeof(proto_channel_table)/sizeof(proto_channel_table[0])
261 * entries for each dev
262 */
263int /* __init */ init_intermezzo_sysctl(void)
264{
265	int i;
266	int total_dev = MAX_CHANNEL;
267	int entries_per_dev = sizeof(proto_psdev_table) /
268		sizeof(proto_psdev_table[0]);
269	int total_entries = entries_per_dev * total_dev;
270	ctl_table *dev_ctl_table;
271
272	PRESTO_ALLOC(dev_ctl_table, sizeof(ctl_table) * total_entries);
273
274	if (! dev_ctl_table) {
275		CERROR("WARNING: presto couldn't allocate dev_ctl_table\n");
276		EXIT;
277		return -ENOMEM;
278	}
279
280	/* now fill in the entries ... we put the individual presto<x>
281	 * entries at the end of the table, and the per-presto stuff
282	 * starting at the front.  We assume that the compiler makes
283	 * this code more efficient, but really, who cares ... it
284	 * happens once per reboot.
285	 */
286	for(i = 0; i < total_dev; i++) {
287		/* entry for this /proc/sys/intermezzo/intermezzo"i" */
288		ctl_table *psdev = &presto_table[i + PRESTO_PRIMARY_CTLCNT];
289		/* entries for the individual "files" in this "directory" */
290		ctl_table *psdev_entries = &dev_ctl_table[i * entries_per_dev];
291		/* init the psdev and psdev_entries with the prototypes */
292		*psdev = proto_channel_entry;
293		memcpy(psdev_entries, proto_psdev_table,
294		       sizeof(proto_psdev_table));
295		/* now specialize them ... */
296		/* the psdev has to point to psdev_entries, and fix the number */
297		psdev->ctl_name = psdev->ctl_name + i + 1; /* sorry */
298
299		PRESTO_ALLOC((void*)psdev->procname, PROCNAME_SIZE);
300		if (!psdev->procname) {
301			PRESTO_FREE(dev_ctl_table,
302				    sizeof(ctl_table) * total_entries);
303			return -ENOMEM;
304		}
305		sprintf((char *) psdev->procname, "intermezzo%d", i);
306		/* hook presto into */
307		psdev->child = psdev_entries;
308
309		/* now for each psdev entry ... */
310		psdev_entries[0].data = &(izo_channels[i].uc_hard);
311		psdev_entries[1].data = &(izo_channels[i].uc_no_filter);
312		psdev_entries[2].data = &(izo_channels[i].uc_no_journal);
313		psdev_entries[3].data = &(izo_channels[i].uc_no_upcall);
314		psdev_entries[4].data = &(izo_channels[i].uc_timeout);
315#ifdef PRESTO_DEBUG
316		psdev_entries[5].data = &(izo_channels[i].uc_errorval);
317#endif
318	}
319
320
321#ifdef CONFIG_SYSCTL
322	if ( !intermezzo_table_header )
323		intermezzo_table_header =
324			register_sysctl_table(intermezzo_table, 0);
325#endif
326#ifdef CONFIG_PROC_FS
327	proc_fs_intermezzo = proc_mkdir("intermezzo", proc_root_fs);
328	proc_fs_intermezzo->owner = THIS_MODULE;
329	create_proc_info_entry("mounts", 0, proc_fs_intermezzo,
330			       intermezzo_mount_get_info);
331#endif
332	return 0;
333}
334
335void cleanup_intermezzo_sysctl(void)
336{
337	int total_dev = MAX_CHANNEL;
338	int entries_per_dev = sizeof(proto_psdev_table) /
339		sizeof(proto_psdev_table[0]);
340	int total_entries = entries_per_dev * total_dev;
341	int i;
342
343#ifdef CONFIG_SYSCTL
344	if ( intermezzo_table_header )
345		unregister_sysctl_table(intermezzo_table_header);
346	intermezzo_table_header = NULL;
347#endif
348	for(i = 0; i < total_dev; i++) {
349		/* entry for this /proc/sys/intermezzo/intermezzo"i" */
350		ctl_table *psdev = &presto_table[i + PRESTO_PRIMARY_CTLCNT];
351		PRESTO_FREE(psdev->procname, PROCNAME_SIZE);
352	}
353	/* presto_table[PRESTO_PRIMARY_CTLCNT].child points to the
354	 * dev_ctl_table previously allocated in init_intermezzo_psdev()
355	 */
356	PRESTO_FREE(presto_table[PRESTO_PRIMARY_CTLCNT].child, sizeof(ctl_table) * total_entries);
357
358#if CONFIG_PROC_FS
359	remove_proc_entry("mounts", proc_fs_intermezzo);
360	remove_proc_entry("intermezzo", proc_root_fs);
361#endif
362}
363
364