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