blob: 65b5bc8936c2540f7300d0f69c5eb5cf755dcd91 [file] [log] [blame]
/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
* vim:expandtab:shiftwidth=8:tabstop=8:
*
* Copyright (C) 1999 Peter J. Braam <braam@clusterfs.com>
*
* This file is part of InterMezzo, http://www.inter-mezzo.org.
*
* InterMezzo is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* InterMezzo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with InterMezzo; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Sysctrl entries for Intermezzo!
*/
#define __NO_VERSION__
#include <linux/config.h> /* for CONFIG_PROC_FS */
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <linux/swapctl.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/stat.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <asm/bitops.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/utsname.h>
#include <linux/blk.h>
#include <linux/intermezzo_fs.h>
#include <linux/intermezzo_psdev.h>
/* /proc entries */
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_fs_intermezzo;
int intermezzo_mount_get_info( char * buffer, char ** start, off_t offset,
int length)
{
int len=0;
/* this works as long as we are below 1024 characters! */
*start = buffer + offset;
len -= offset;
if ( len < 0 )
return -EINVAL;
return len;
}
#endif
/* SYSCTL below */
static struct ctl_table_header *intermezzo_table_header = NULL;
/* 0x100 to avoid any chance of collisions at any point in the tree with
* non-directories
*/
#define PSDEV_INTERMEZZO (0x100)
#define PSDEV_DEBUG 1 /* control debugging */
#define PSDEV_TRACE 2 /* control enter/leave pattern */
#define PSDEV_TIMEOUT 3 /* timeout on upcalls to become intrble */
#define PSDEV_HARD 4 /* mount type "hard" or "soft" */
#define PSDEV_NO_FILTER 5 /* controls presto_chk */
#define PSDEV_NO_JOURNAL 6 /* controls presto_chk */
#define PSDEV_NO_UPCALL 7 /* controls lento_upcall */
#define PSDEV_ERRORVAL 8 /* controls presto_debug_fail_blkdev */
#define PSDEV_EXCL_GID 9 /* which GID is ignored by presto */
#define PSDEV_BYTES_TO_CLOSE 11 /* bytes to write before close */
/* These are global presto control options */
#define PRESTO_PRIMARY_CTLCNT 2
static struct ctl_table presto_table[ PRESTO_PRIMARY_CTLCNT + MAX_CHANNEL + 1] =
{
{PSDEV_DEBUG, "debug", &presto_debug, sizeof(int), 0644, NULL, &proc_dointvec},
{PSDEV_TRACE, "trace", &presto_print_entry, sizeof(int), 0644, NULL, &proc_dointvec},
};
/*
* Intalling the sysctl entries: strategy
* - have templates for each /proc/sys/intermezzo/ entry
* such an entry exists for each /dev/presto
* (proto_channel_entry)
* - have a template for the contents of such directories
* (proto_psdev_table)
* - have the master table (presto_table)
*
* When installing, malloc, memcpy and fix up the pointers to point to
* the appropriate constants in izo_channels[your_minor]
*/
static ctl_table proto_psdev_table[] = {
{PSDEV_HARD, "hard", 0, sizeof(int), 0644, NULL, &proc_dointvec},
{PSDEV_NO_FILTER, "no_filter", 0, sizeof(int), 0644, NULL, &proc_dointvec},
{PSDEV_NO_JOURNAL, "no_journal", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
{PSDEV_NO_UPCALL, "no_upcall", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
{PSDEV_TIMEOUT, "timeout", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
#ifdef PRESTO_DEBUG
{PSDEV_ERRORVAL, "errorval", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
#endif
{ 0 }
};
static ctl_table proto_channel_entry = {
PSDEV_INTERMEZZO, 0, NULL, 0, 0555, 0,
};
static ctl_table intermezzo_table[2] = {
{PSDEV_INTERMEZZO, "intermezzo", NULL, 0, 0555, presto_table},
{0}
};
/* support for external setting and getting of opts. */
/* particularly via ioctl. The Right way to do this is via sysctl,
* but that will have to wait until intermezzo gets its own nice set of
* sysctl IDs
*/
/* we made these separate as setting may in future be more restricted
* than getting
*/
#ifdef RON_MINNICH
int dosetopt(int minor, struct psdev_opt *opt)
{
int retval = 0;
int newval = opt->optval;
ENTRY;
switch(opt->optname) {
case PSDEV_TIMEOUT:
izo_channels[minor].uc_timeout = newval;
break;
case PSDEV_HARD:
izo_channels[minor].uc_hard = newval;
break;
case PSDEV_NO_FILTER:
izo_channels[minor].uc_no_filter = newval;
break;
case PSDEV_NO_JOURNAL:
izo_channels[minor].uc_no_journal = newval;
break;
case PSDEV_NO_UPCALL:
izo_channels[minor].uc_no_upcall = newval;
break;
#ifdef PRESTO_DEBUG
case PSDEV_ERRORVAL: {
/* If we have a positive arg, set a breakpoint for that
* value. If we have a negative arg, make that device
* read-only. FIXME It would be much better to only
* allow setting the underlying device read-only for the
* current presto cache.
*/
int errorval = izo_channels[minor].uc_errorval;
if (errorval < 0) {
if (newval == 0)
set_device_ro(-errorval, 0);
else
CERROR("device %s already read only\n",
kdevname(-errorval));
} else {
if (newval < 0)
set_device_ro(-newval, 1);
izo_channels[minor].uc_errorval = newval;
CDEBUG(D_PSDEV, "setting errorval to %d\n", newval);
}
break;
}
#endif
case PSDEV_TRACE:
case PSDEV_DEBUG:
case PSDEV_BYTES_TO_CLOSE:
default:
CDEBUG(D_PSDEV,
"ioctl: dosetopt: minor %d, bad optname 0x%x, \n",
minor, opt->optname);
retval = -EINVAL;
}
EXIT;
return retval;
}
int dogetopt(int minor, struct psdev_opt *opt)
{
int retval = 0;
ENTRY;
switch(opt->optname) {
case PSDEV_TIMEOUT:
opt->optval = izo_channels[minor].uc_timeout;
break;
case PSDEV_HARD:
opt->optval = izo_channels[minor].uc_hard;
break;
case PSDEV_NO_FILTER:
opt->optval = izo_channels[minor].uc_no_filter;
break;
case PSDEV_NO_JOURNAL:
opt->optval = izo_channels[minor].uc_no_journal;
break;
case PSDEV_NO_UPCALL:
opt->optval = izo_channels[minor].uc_no_upcall;
break;
#ifdef PSDEV_DEBUG
case PSDEV_ERRORVAL: {
int errorval = izo_channels[minor].uc_errorval;
if (errorval < 0 && is_read_only(-errorval))
CERROR("device %s has been set read-only\n",
kdevname(-errorval));
opt->optval = izo_channels[minor].uc_errorval;
break;
}
#endif
case PSDEV_TRACE:
case PSDEV_DEBUG:
case PSDEV_BYTES_TO_CLOSE:
default:
CDEBUG(D_PSDEV,
"ioctl: dogetopt: minor %d, bad optval 0x%x, \n",
minor, opt->optname);
retval = -EINVAL;
}
EXIT;
return retval;
}
#endif
/* allocate the tables for the presto devices. We need
* sizeof(proto_channel_table)/sizeof(proto_channel_table[0])
* entries for each dev
*/
int /* __init */ init_intermezzo_sysctl(void)
{
int i;
int total_dev = MAX_CHANNEL;
int entries_per_dev = sizeof(proto_psdev_table) /
sizeof(proto_psdev_table[0]);
int total_entries = entries_per_dev * total_dev;
ctl_table *dev_ctl_table;
PRESTO_ALLOC(dev_ctl_table, sizeof(ctl_table) * total_entries);
if (! dev_ctl_table) {
CERROR("WARNING: presto couldn't allocate dev_ctl_table\n");
EXIT;
return -ENOMEM;
}
/* now fill in the entries ... we put the individual presto<x>
* entries at the end of the table, and the per-presto stuff
* starting at the front. We assume that the compiler makes
* this code more efficient, but really, who cares ... it
* happens once per reboot.
*/
for(i = 0; i < total_dev; i++) {
void *p;
/* entry for this /proc/sys/intermezzo/intermezzo"i" */
ctl_table *psdev = &presto_table[i + PRESTO_PRIMARY_CTLCNT];
/* entries for the individual "files" in this "directory" */
ctl_table *psdev_entries = &dev_ctl_table[i * entries_per_dev];
/* init the psdev and psdev_entries with the prototypes */
*psdev = proto_channel_entry;
memcpy(psdev_entries, proto_psdev_table,
sizeof(proto_psdev_table));
/* now specialize them ... */
/* the psdev has to point to psdev_entries, and fix the number */
psdev->ctl_name = psdev->ctl_name + i + 1; /* sorry */
PRESTO_ALLOC(p, PROCNAME_SIZE);
psdev->procname = p;
if (!psdev->procname) {
PRESTO_FREE(dev_ctl_table,
sizeof(ctl_table) * total_entries);
return -ENOMEM;
}
sprintf((char *) psdev->procname, "intermezzo%d", i);
/* hook presto into */
psdev->child = psdev_entries;
/* now for each psdev entry ... */
psdev_entries[0].data = &(izo_channels[i].uc_hard);
psdev_entries[1].data = &(izo_channels[i].uc_no_filter);
psdev_entries[2].data = &(izo_channels[i].uc_no_journal);
psdev_entries[3].data = &(izo_channels[i].uc_no_upcall);
psdev_entries[4].data = &(izo_channels[i].uc_timeout);
#ifdef PRESTO_DEBUG
psdev_entries[5].data = &(izo_channels[i].uc_errorval);
#endif
}
#ifdef CONFIG_SYSCTL
if ( !intermezzo_table_header )
intermezzo_table_header =
register_sysctl_table(intermezzo_table, 0);
#endif
#ifdef CONFIG_PROC_FS
proc_fs_intermezzo = proc_mkdir("intermezzo", proc_root_fs);
proc_fs_intermezzo->owner = THIS_MODULE;
create_proc_info_entry("mounts", 0, proc_fs_intermezzo,
intermezzo_mount_get_info);
#endif
return 0;
}
void cleanup_intermezzo_sysctl(void)
{
int total_dev = MAX_CHANNEL;
int entries_per_dev = sizeof(proto_psdev_table) /
sizeof(proto_psdev_table[0]);
int total_entries = entries_per_dev * total_dev;
int i;
#ifdef CONFIG_SYSCTL
if ( intermezzo_table_header )
unregister_sysctl_table(intermezzo_table_header);
intermezzo_table_header = NULL;
#endif
for(i = 0; i < total_dev; i++) {
/* entry for this /proc/sys/intermezzo/intermezzo"i" */
ctl_table *psdev = &presto_table[i + PRESTO_PRIMARY_CTLCNT];
PRESTO_FREE(psdev->procname, PROCNAME_SIZE);
}
/* presto_table[PRESTO_PRIMARY_CTLCNT].child points to the
* dev_ctl_table previously allocated in init_intermezzo_psdev()
*/
PRESTO_FREE(presto_table[PRESTO_PRIMARY_CTLCNT].child, sizeof(ctl_table) * total_entries);
#if CONFIG_PROC_FS
remove_proc_entry("mounts", proc_fs_intermezzo);
remove_proc_entry("intermezzo", proc_root_fs);
#endif
}