Commit 063b009f authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] nfsd as filesystem

* introduces a new filesystem - nfsd.  No, it's not a typo.  It's a small
  tree with fixed topology defined by nfsd and IO on its files does what
  we used to do by hand in nfsctl.c.
* turns sys_nfsservctl() into a sequence of open()/write()/read()/close()
  It works as it used to - we don't need nfsd to be mounted anywhere, etc.
* nfsd_linkage ugliness is gone.
* getfs and getfh demonstrate (rather trivial) example of "descriptor as
  transaction descriptor" behaviour.
* we are fairly close to the situation when driver-defined filesystems can
  be done with practically zero code overhead.  We are still not there, but
  it's a matter of adding a couple of helpers for populating the tree.

	One thing we get immediately is a cleanup of sys_nfsservctl() -
it got _much_ better.  Moreover, we get an alternative interface that
uses normal file IO and can be used without magic syscalls.
parent edb1bea5
......@@ -22,6 +22,12 @@ else
obj-y += noquot.o
endif
ifneq ($(CONFIG_NFSD),n)
ifneq ($(CONFIG_NFSD),)
obj-y += nfsctl.o
endif
endif
subdir-$(CONFIG_PROC_FS) += proc
subdir-y += partitions
subdir-y += driverfs
......
......@@ -1252,6 +1252,7 @@ kmem_cache_t *dquot_cachep;
/* SLAB cache for buffer_head structures */
kmem_cache_t *bh_cachep;
EXPORT_SYMBOL(bh_cachep);
EXPORT_SYMBOL(d_genocide);
extern void bdev_cache_init(void);
extern void cdev_cache_init(void);
......
......@@ -3,39 +3,10 @@
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* nfsservctl system-call when nfsd is not compiled in.
* table of configured filesystems
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/smp_lock.h>
#include <linux/kmod.h>
#include <linux/nfsd/interface.h>
#include <linux/linkage.h>
#if ! defined(CONFIG_NFSD)
struct nfsd_linkage *nfsd_linkage;
long
asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp)
{
int ret = -ENOSYS;
#if defined(CONFIG_MODULES)
lock_kernel();
if (nfsd_linkage ||
(request_module ("nfsd") == 0 && nfsd_linkage)) {
__MOD_INC_USE_COUNT(nfsd_linkage->owner);
unlock_kernel();
ret = nfsd_linkage->do_nfsservctl(cmd, argp, resp);
__MOD_DEC_USE_COUNT(nfsd_linkage->owner);
} else
unlock_kernel();
#endif
return ret;
}
EXPORT_SYMBOL(nfsd_linkage);
#endif /* CONFIG_NFSD */
/*
* Code will move here from fs/super.c and yes, it will be fs type handling
* stuff.
*/
/*
* fs/nfsctl.c
*
* This should eventually move to userland.
*
*/
#include <linux/config.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/syscall.h>
#include <linux/linkage.h>
#include <asm/uaccess.h>
/*
* open a file on nfsd fs
*/
struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data);
static struct file *do_open(char *name, int flags)
{
struct nameidata nd;
int error;
nd.mnt = do_kern_mount("nfsd", 0, "nfsd", NULL);
if (IS_ERR(nd.mnt))
return (struct file *)nd.mnt;
nd.dentry = dget(nd.mnt->mnt_root);
nd.last_type = LAST_ROOT;
nd.flags = 0;
error = path_walk(name, &nd);
if (error)
return ERR_PTR(error);
return dentry_open(nd.dentry, nd.mnt, flags);
}
static struct {
char *name; int wsize; int rsize;
} map[] = {
[NFSCTL_SVC]={"svc", sizeof(struct nfsctl_svc)},
[NFSCTL_ADDCLIENT]={"add", sizeof(struct nfsctl_client)},
[NFSCTL_DELCLIENT]={"del", sizeof(struct nfsctl_client)},
[NFSCTL_EXPORT]={"export", sizeof(struct nfsctl_export)},
[NFSCTL_UNEXPORT]={"unexport", sizeof(struct nfsctl_export)},
#ifdef notyet
[NFSCTL_UGIDUPDATE]={"ugid", sizeof(struct nfsctl_uidmap)},
#endif
[NFSCTL_GETFD]={"getfd", sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
[NFSCTL_GETFS]={"getfs", sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
};
long
asmlinkage sys_nfsservctl(int cmd, struct nfsctl_arg *arg, void *res)
{
struct file *file;
void *p = &arg->u;
int version;
int err;
if (copy_from_user(&version, &arg->ca_version, sizeof(int)))
return -EFAULT;
if (version != NFSCTL_VERSION) {
printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
return -EINVAL;
}
if (cmd < 0 || cmd >= sizeof(map)/sizeof(map[0]) || !map[cmd].name)
return -EINVAL;
file = do_open(map[cmd].name, map[cmd].rsize ? O_RDWR : O_WRONLY);
if (IS_ERR(file))
return PTR_ERR(file);
err = file->f_op->write(file, p, map[cmd].wsize, &file->f_pos);
if (err >= 0 && map[cmd].rsize)
err = file->f_op->read(file, res, map[cmd].rsize, &file->f_pos);
if (err >= 0)
err = 0;
fput(file);
return err;
}
......@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/pagemap.h>
#include <linux/nfs.h>
#include <linux/sunrpc/svc.h>
......@@ -30,20 +31,96 @@
#include <linux/nfsd/syscall.h>
#include <asm/uaccess.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
static int nfsctl_svc(struct nfsctl_svc *data);
static int nfsctl_addclient(struct nfsctl_client *data);
static int nfsctl_delclient(struct nfsctl_client *data);
static int nfsctl_export(struct nfsctl_export *data);
static int nfsctl_unexport(struct nfsctl_export *data);
static int nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
static int nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
#ifdef notyet
static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
#endif
/*
* We have a single directory with 8 nodes in it.
*/
enum {
NFSD_Root = 1,
NFSD_Svc,
NFSD_Add,
NFSD_Del,
NFSD_Export,
NFSD_Unexport,
NFSD_Getfd,
NFSD_Getfs,
NFSD_List,
};
/*
* write() for these nodes.
*/
static ssize_t write_svc(struct file *file, const char *buf, size_t size);
static ssize_t write_add(struct file *file, const char *buf, size_t size);
static ssize_t write_del(struct file *file, const char *buf, size_t size);
static ssize_t write_export(struct file *file, const char *buf, size_t size);
static ssize_t write_unexport(struct file *file, const char *buf, size_t size);
static ssize_t write_getfd(struct file *file, const char *buf, size_t size);
static ssize_t write_getfs(struct file *file, const char *buf, size_t size);
static ssize_t (*write_op[])(struct file *, const char *, size_t) = {
[NFSD_Svc] = write_svc,
[NFSD_Add] = write_add,
[NFSD_Del] = write_del,
[NFSD_Export] = write_export,
[NFSD_Unexport] = write_unexport,
[NFSD_Getfd] = write_getfd,
[NFSD_Getfs] = write_getfs,
};
static ssize_t fs_write(struct file *file, const char *buf, size_t size, loff_t *pos)
{
ino_t ino = file->f_dentry->d_inode->i_ino;
if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
return -EINVAL;
return write_op[ino](file, buf, size);
}
/*
* read(), open() and release() for getfs and getfd (read/write ones).
* IO on these is a simple transaction - you open() the file, write() to it
* and that generates a (stored) response. After that read() will simply
* access that response.
*/
static ssize_t TA_read(struct file *file, char *buf, size_t size, loff_t *pos)
{
if (!file->private_data)
return 0;
if (*pos >= file->f_dentry->d_inode->i_size)
return 0;
if (*pos + size > file->f_dentry->d_inode->i_size)
size = file->f_dentry->d_inode->i_size - *pos;
if (copy_to_user(buf, file->private_data + *pos, size))
return -EFAULT;
*pos += size;
return size;
}
static ssize_t TA_open(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static ssize_t TA_release(struct inode *inode, struct file *file)
{
void *p = file->private_data;
file->private_data = NULL;
kfree(p);
return 0;
}
static struct file_operations writer_ops = {
write: fs_write,
};
static struct file_operations reader_ops = {
write: fs_write,
read: TA_read,
open: TA_open,
release:TA_release,
};
extern struct seq_operations nfs_exports_op;
static int exports_open(struct inode *inode, struct file *file)
......@@ -57,254 +134,277 @@ static struct file_operations exports_operations = {
release: seq_release,
};
void proc_export_init(void)
{
struct proc_dir_entry *entry;
if (!proc_mkdir("fs/nfs", 0))
return;
entry = create_proc_entry("fs/nfs/exports", 0, NULL);
if (entry)
entry->proc_fops = &exports_operations;
}
/*
* Description of fs contents.
*/
static struct { char *name; struct file_operations *ops; int mode; } files[] = {
[NFSD_Svc] = {"svc", &writer_ops, S_IWUSR},
[NFSD_Add] = {"add", &writer_ops, S_IWUSR},
[NFSD_Del] = {"del", &writer_ops, S_IWUSR},
[NFSD_Export] = {"export", &writer_ops, S_IWUSR},
[NFSD_Unexport] = {"unexport", &writer_ops, S_IWUSR},
[NFSD_Getfd] = {"getfd", &reader_ops, S_IWUSR|S_IRUSR},
[NFSD_Getfs] = {"getfs", &reader_ops, S_IWUSR|S_IRUSR},
[NFSD_List] = {"exports", &exports_operations, S_IRUGO},
};
static inline int
nfsctl_svc(struct nfsctl_svc *data)
{
return nfsd_svc(data->svc_port, data->svc_nthreads);
}
/*----------------------------------------------------------------------------*/
/*
* payload - write methods
*/
static inline int
nfsctl_addclient(struct nfsctl_client *data)
static ssize_t write_svc(struct file *file, const char *buf, size_t size)
{
return exp_addclient(data);
struct nfsctl_svc data;
if (size < sizeof(data))
return -EINVAL;
if (copy_from_user(&data, buf, size))
return -EFAULT;
return nfsd_svc(data.svc_port, data.svc_nthreads);
}
static inline int
nfsctl_delclient(struct nfsctl_client *data)
static ssize_t write_add(struct file *file, const char *buf, size_t size)
{
return exp_delclient(data);
struct nfsctl_client data;
if (size < sizeof(data))
return -EINVAL;
if (copy_from_user(&data, buf, size))
return -EFAULT;
return exp_addclient(&data);
}
static inline int
nfsctl_export(struct nfsctl_export *data)
static ssize_t write_del(struct file *file, const char *buf, size_t size)
{
return exp_export(data);
struct nfsctl_client data;
if (size < sizeof(data))
return -EINVAL;
if (copy_from_user(&data, buf, size))
return -EFAULT;
return exp_delclient(&data);
}
static inline int
nfsctl_unexport(struct nfsctl_export *data)
static ssize_t write_export(struct file *file, const char *buf, size_t size)
{
return exp_unexport(data);
struct nfsctl_export data;
if (size < sizeof(data))
return -EINVAL;
if (copy_from_user(&data, buf, size))
return -EFAULT;
return exp_export(&data);
}
#ifdef notyet
static inline int
nfsctl_ugidupdate(nfs_ugidmap *data)
static ssize_t write_unexport(struct file *file, const char *buf, size_t size)
{
struct nfsctl_export data;
if (size < sizeof(data))
return -EINVAL;
if (copy_from_user(&data, buf, size))
return -EFAULT;
return exp_unexport(&data);
}
#endif
static inline int
nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
static ssize_t write_getfs(struct file *file, const char *buf, size_t size)
{
struct nfsctl_fsparm data;
struct sockaddr_in *sin;
struct svc_client *clp;
int err = 0;
struct knfsd_fh *res;
if (data->gd_addr.sa_family != AF_INET)
if (file->private_data)
return -EINVAL;
if (size < sizeof(data))
return -EINVAL;
if (copy_from_user(&data, buf, size))
return -EFAULT;
if (data.gd_addr.sa_family != AF_INET)
return -EPROTONOSUPPORT;
sin = (struct sockaddr_in *)&data->gd_addr;
if (data->gd_maxlen > NFS3_FHSIZE)
data->gd_maxlen = NFS3_FHSIZE;
sin = (struct sockaddr_in *)&data.gd_addr;
if (data.gd_maxlen > NFS3_FHSIZE)
data.gd_maxlen = NFS3_FHSIZE;
res = kmalloc(sizeof(struct knfsd_fh), GFP_KERNEL);
memset(res, 0, sizeof(struct knfsd_fh));
if (!res)
return -ENOMEM;
exp_readlock();
if (!(clp = exp_getclient(sin)))
err = -EPERM;
else
err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
err = exp_rootfh(clp, data.gd_path, res, data.gd_maxlen);
exp_readunlock();
down(&file->f_dentry->d_inode->i_sem);
if (file->private_data)
err = -EINVAL;
if (err)
kfree(res);
else {
file->f_dentry->d_inode->i_size = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base;
file->private_data = res;
err = sizeof(data);
}
up(&file->f_dentry->d_inode->i_sem);
return err;
}
static inline int
nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
static ssize_t write_getfd(struct file *file, const char *buf, size_t size)
{
struct nfsctl_fdparm data;
struct sockaddr_in *sin;
struct svc_client *clp;
int err = 0;
struct knfsd_fh fh;
char *res;
if (data->gd_addr.sa_family != AF_INET)
if (file->private_data)
return -EINVAL;
if (size < sizeof(data))
return -EINVAL;
if (copy_from_user(&data, buf, size))
return -EFAULT;
if (data.gd_addr.sa_family != AF_INET)
return -EPROTONOSUPPORT;
if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
if (data.gd_version < 2 || data.gd_version > NFSSVC_MAXVERS)
return -EINVAL;
sin = (struct sockaddr_in *)&data->gd_addr;
res = kmalloc(NFS_FHSIZE, GFP_KERNEL);
if (!res)
return -ENOMEM;
sin = (struct sockaddr_in *)&data.gd_addr;
exp_readlock();
if (!(clp = exp_getclient(sin)))
err = -EPERM;
else
err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
err = exp_rootfh(clp, data.gd_path, &fh, NFS_FHSIZE);
exp_readunlock();
if (err == 0) {
if (fh.fh_size > NFS_FHSIZE)
down(&file->f_dentry->d_inode->i_sem);
if (file->private_data)
err = -EINVAL;
if (!err && fh.fh_size > NFS_FHSIZE)
err = -EINVAL;
if (err)
kfree(res);
else {
memset(res,0, NFS_FHSIZE);
memcpy(res, &fh.fh_base, fh.fh_size);
file->f_dentry->d_inode->i_size = NFS_FHSIZE;
file->private_data = res;
err = sizeof(data);
}
}
up(&file->f_dentry->d_inode->i_sem);
return err;
}
#ifdef CONFIG_NFSD
#define handle_sys_nfsservctl sys_nfsservctl
#endif
static struct {
int argsize, respsize;
} sizes[] = {
/* NFSCTL_SVC */ { sizeof(struct nfsctl_svc), 0 },
/* NFSCTL_ADDCLIENT */ { sizeof(struct nfsctl_client), 0},
/* NFSCTL_DELCLIENT */ { sizeof(struct nfsctl_client), 0},
/* NFSCTL_EXPORT */ { sizeof(struct nfsctl_export), 0},
/* NFSCTL_UNEXPORT */ { sizeof(struct nfsctl_export), 0},
/* NFSCTL_UGIDUPDATE */ { sizeof(struct nfsctl_uidmap), 0},
/* NFSCTL_GETFH */ { sizeof(struct nfsctl_fhparm), NFS_FHSIZE},
/* NFSCTL_GETFD */ { sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
/* NFSCTL_GETFS */ { sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
/*----------------------------------------------------------------------------*/
/*
* populating the filesystem.
*/
static struct super_operations s_ops = {
statfs: simple_statfs,
};
#define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
long
asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
{
struct nfsctl_arg * argp = opaque_argp;
union nfsctl_res * resp = opaque_resp;
struct nfsctl_arg * arg = NULL;
union nfsctl_res * res = NULL;
int err;
int argsize, respsize;
err = -EPERM;
if (!capable(CAP_SYS_ADMIN)) {
goto done;
struct inode *inode;
struct dentry *root;
struct dentry *dentry;
int i;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = 0x6e667364;
sb->s_op = &s_ops;
inode = new_inode(sb);
if (!inode)
return -ENOMEM;
inode->i_mode = S_IFDIR | 0755;
inode->i_uid = inode->i_gid = 0;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
root = d_alloc_root(inode);
if (!root) {
iput(inode);
return -ENOMEM;
}
err = -EINVAL;
if (cmd<0 || cmd > CMD_MAX)
goto done;
err = -EFAULT;
argsize = sizes[cmd].argsize + (int)&((struct nfsctl_arg *)0)->u;
respsize = sizes[cmd].respsize; /* maximum */
if (!access_ok(VERIFY_READ, argp, argsize)
|| (resp && !access_ok(VERIFY_WRITE, resp, respsize))) {
goto done;
}
err = -ENOMEM; /* ??? */
if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
(resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
goto done;
}
err = -EINVAL;
copy_from_user(arg, argp, argsize);
if (arg->ca_version != NFSCTL_VERSION) {
printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
goto done;
for (i = NFSD_Svc; i <= NFSD_List; i++) {
struct qstr name;
name.name = files[i].name;
name.len = strlen(name.name);
name.hash = full_name_hash(name.name, name.len);
dentry = d_alloc(root, &name);
if (!dentry)
goto out;
inode = new_inode(sb);
if (!inode)
goto out;
inode->i_mode = S_IFREG | files[i].mode;
inode->i_uid = inode->i_gid = 0;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_fop = files[i].ops;
inode->i_ino = i;
d_add(dentry, inode);
}
sb->s_root = root;
return 0;
switch(cmd) {
case NFSCTL_SVC:
err = nfsctl_svc(&arg->ca_svc);
break;
case NFSCTL_ADDCLIENT:
err = nfsctl_addclient(&arg->ca_client);
break;
case NFSCTL_DELCLIENT:
err = nfsctl_delclient(&arg->ca_client);
break;
case NFSCTL_EXPORT:
err = nfsctl_export(&arg->ca_export);
break;
case NFSCTL_UNEXPORT:
err = nfsctl_unexport(&arg->ca_export);
break;
#ifdef notyet
case NFSCTL_UGIDUPDATE:
err = nfsctl_ugidupdate(&arg->ca_umap);
break;
#endif
case NFSCTL_GETFD:
err = nfsctl_getfd(&arg->ca_getfd, res->cr_getfh);
break;
case NFSCTL_GETFS:
err = nfsctl_getfs(&arg->ca_getfs, &res->cr_getfs);
respsize = res->cr_getfs.fh_size+ (int)&((struct knfsd_fh*)0)->fh_base;
break;
default:
err = -EINVAL;
}
if (!err && resp && respsize)
copy_to_user(resp, res, respsize);
done:
if (arg)
kfree(arg);
if (res)
kfree(res);
return err;
out:
d_genocide(root);
dput(root);
return -ENOMEM;
}
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
MODULE_LICENSE("GPL");
static struct super_block *nfsd_get_sb(struct file_system_type *fs_type,
int flags, char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, nfsd_fill_super);
}
#ifdef MODULE
struct nfsd_linkage nfsd_linkage_s = {
do_nfsservctl: handle_sys_nfsservctl,
static struct file_system_type nfsd_fs_type = {
owner: THIS_MODULE,
name: "nfsd",
get_sb: nfsd_get_sb,
kill_sb: kill_litter_super,
};
#endif
/*
* Initialize the module
*/
static int __init
nfsd_init(void)
static int __init init_nfsd(void)
{
printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
#ifdef MODULE
nfsd_linkage = &nfsd_linkage_s;
#endif
nfsd_stat_init(); /* Statistics */
nfsd_cache_init(); /* RPC reply cache */
nfsd_export_init(); /* Exports table */
nfsd_lockd_init(); /* lockd->nfsd callbacks */
proc_export_init();
if (proc_mkdir("fs/nfs", 0)) {
struct proc_dir_entry *entry;
entry = create_proc_entry("fs/nfs/exports", 0, NULL);
if (entry)
entry->proc_fops = &exports_operations;
}
register_filesystem(&nfsd_fs_type);
return 0;
}
/*
* Clean up the mess before unloading the module
*/
static void __exit
nfsd_exit(void)
static void __exit exit_nfsd(void)
{
#ifdef MODULE
nfsd_linkage = NULL;
#endif
nfsd_export_shutdown();
nfsd_cache_shutdown();
remove_proc_entry("fs/nfs/exports", NULL);
remove_proc_entry("fs/nfs", NULL);
nfsd_stat_shutdown();
nfsd_lockd_shutdown();
unregister_filesystem(&nfsd_fs_type);
}
module_init(nfsd_init);
module_exit(nfsd_exit);
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
MODULE_LICENSE("GPL");
module_init(init_nfsd)
module_exit(exit_nfsd)
......@@ -10,17 +10,4 @@
#ifndef LINUX_NFSD_INTERFACE_H
#define LINUX_NFSD_INTERFACE_H
#include <linux/config.h>
#ifndef CONFIG_NFSD
#ifdef CONFIG_MODULES
extern struct nfsd_linkage {
long (*do_nfsservctl)(int cmd, void *argp, void *resp);
struct module *owner;
} * nfsd_linkage;
#endif
#endif
#endif /* LINUX_NFSD_INTERFACE_H */
......@@ -133,7 +133,7 @@ union nfsctl_res {
* Kernel syscall implementation.
*/
#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
extern asmlinkage long sys_nfsservctl(int, void *, void *);
extern asmlinkage long sys_nfsservctl(int, struct nfsctl_arg *, void *);
#else
#define sys_nfsservctl sys_ni_syscall
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment