Re: [RFC] dynamic syscalls revisited

From: Steven Rostedt
Date: Mon Dec 06 2004 - 11:19:17 EST


On Mon, 2004-12-06 at 00:46 +0100, Adrian Bunk wrote:
>
> Why don't you EXPORT_SYMBOL_GPL dsyscall_{,un}register?
>
> This should at least fix the binary only module concerns.

Done!

Updated on http://home.stny.rr.com/rostedt/dynamic as well as included
in this email for ease.

Now, I guess you can still get around this if the "Bad Vendor" were to
write a GPL module with their added system calls, and have that module
include hooks to their binary module. So, until we can fix that, I
guess Linus won't allow for this module to be included in the main line.

-- Steve


Index: kernel/Makefile
===================================================================
--- kernel/Makefile (revision 15)
+++ kernel/Makefile (working copy)
@@ -29,6 +29,7 @@
obj-$(CONFIG_SYSFS) += ksysfs.o
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
obj-$(CONFIG_CRASH_DUMP) += crash.o
+obj-$(CONFIG_DSYSCALL) += dsyscall.o

ifneq ($(CONFIG_IA64),y)
# According to Alan Modra <alan@xxxxxxxxxxxxxxxx>, the -fno-omit-frame-pointer is
Index: kernel/dsyscall.c
===================================================================
--- kernel/dsyscall.c (revision 0)
+++ kernel/dsyscall.c (revision 0)
@@ -0,0 +1,286 @@
+/*
+ * dsyscall.c
+ *
+ * Copyright (C) 2004 Steven Rostedt <steven.rostedt@xxxxxxxxxxxxx>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/dsyscall.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+
+#include <asm/uaccess.h>
+
+#undef DEBUG
+#ifdef DEBUG
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...) do { } while(0)
+#endif
+
+#define DSYSCALL_HASH_SIZE 32
+
+DECLARE_RWSEM(dsyscall_sem);
+
+static long dsyscall_next_id = 0;
+static long dsyscall_iterations = 0;
+static kmem_cache_t *dsyscall_cache;
+static struct list_head dsyscall_hash[DSYSCALL_HASH_SIZE];
+static LIST_HEAD(dsyscall_syscalls);
+
+static int dsyscall_hash_add(struct dsyscall_struct *ds)
+{
+ list_add(&ds->link,&dsyscall_hash[ds->id & (DSYSCALL_HASH_SIZE - 1)]);
+ return 0;
+}
+
+static struct dsyscall_struct * find_dsyscall_by_name(char *name)
+{
+ struct list_head *li;
+ struct dsyscall_struct *ds, *ret = NULL;
+
+ /* slow, but this should not be called often. */
+ list_for_each(li,&dsyscall_syscalls) {
+ ds = list_entry(li, struct dsyscall_struct, next);
+ if (strcmp(ds->name,name) == 0) {
+ ret = ds;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static struct dsyscall_struct * find_dsyscall_by_id(struct dsyscall *d)
+{
+ struct list_head *li;
+ struct dsyscall_struct *ds, *ret = NULL;
+
+ /* For now it's a little faster. We can optimize this if we need to */
+ list_for_each(li,&dsyscall_hash[d->id & (DSYSCALL_HASH_SIZE - 1)]) {
+ ds = list_entry(li, struct dsyscall_struct, link);
+ if (ds->id == d->id) {
+ if (likely(ds->iteration == d->iteration))
+ ret = ds;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+int sys_dsyscall(int type, char *user_name, struct dsyscall *dcall)
+{
+ int ret;
+ struct dsyscall_struct *ds;
+ struct dsyscall d;
+ long argv[DSYSCALL_MAX_ARGS];
+
+ /*
+ * OK, this is pretty long to hold a semaphore, even if
+ * if is just for reading. But if you want speed, don't use
+ * dynamic system calls!
+ */
+ down_read(&dsyscall_sem);
+ switch (type) {
+ case DSYSCALL_GET:
+ dprintk("sys_dsyscall: DSYSCALL_GET, ");
+ memset (&d,0,sizeof(d));
+ ret = -EFAULT;
+ if (strncpy_from_user(d.name,user_name,DSYSCALL_NAME_SZ-1) < 0) {
+ dprintk(">>>BAD NAME<<<\n");
+ goto out;
+ }
+ dprintk("%s: ", d.name);
+
+ /* just in case */
+ d.name[DSYSCALL_NAME_SZ-1] = 0;
+
+ ds = find_dsyscall_by_name(d.name);
+
+ if (!ds) {
+ dprintk("not found\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ dprintk("found\n");
+ d.id = ds->id;
+ d.iteration = ds->iteration;
+ d.argc = ds->args;
+
+ ret = 0;
+ if (copy_to_user(dcall,&d,sizeof(d)) != 0) {
+ ret = -EFAULT;
+ }
+ break;
+
+ case DSYSCALL_CALL:
+ ret = -EFAULT;
+ if (copy_from_user(&d,dcall,sizeof(d)) != 0) {
+ goto out;
+ }
+
+ ds = find_dsyscall_by_id(&d);
+ if (!ds) {
+ ret = -ENODEV;
+ goto out;
+ }
+ /* Comparing to ds->args which can not be bigger than DSYSCALL_MAX_ARGS */
+ if (d.argc != ds->args) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (d.argc > 0) {
+ if (copy_from_user(argv,d.argv,sizeof(char*)*d.argc) != 0) {
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+
+ switch (d.argc) {
+ case 0:
+ ret = ds->func(0); /* The argument should be ignored */
+ break;
+ case 1:
+ ret = ds->func(argv[0]);
+ break;
+ case 2:
+ ret = ds->func(argv[0],argv[1]);
+ break;
+ case 3:
+ ret = ds->func(argv[0],argv[1],argv[2]);
+ break;
+ case 4:
+ ret = ds->func(argv[0],argv[1],argv[2],argv[3]);
+ break;
+ case 5:
+ ret = ds->func(argv[0],argv[1],argv[2],argv[3],argv[4]);
+ break;
+ case 6:
+ ret = ds->func(argv[0],argv[1],argv[2],argv[3],argv[4],argv[5]);
+ break;
+ }
+ goto out;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ out:
+ up_read(&dsyscall_sem);
+
+ return ret;
+}
+
+int dsyscall_register(char *name, int args, int (*func)(long,...))
+{
+ int ret = -1;
+ struct dsyscall_struct *ds;
+
+ dprintk("dsyscall_register: %s args: %d func=%p\n",name,args,func);
+
+ if (strlen(name) >= DSYSCALL_NAME_SZ) {
+ printk(KERN_INFO "dsyscall: name %s is too large.", name);
+ return -1;
+ }
+ if (args > DSYSCALL_MAX_ARGS) {
+ printk(KERN_INFO "dsyscall: args is too big.");
+ return -1;
+ }
+
+ down_write(&dsyscall_sem);
+ if ((ds = find_dsyscall_by_name(name)) != NULL) {
+ printk(KERN_INFO "dsyscall: name %s is already in use.",name);
+ goto out;
+ }
+
+ ds = kmem_cache_alloc(dsyscall_cache,GFP_KERNEL);
+ if (!ds) {
+ printk(KERN_INFO "dsyscall: could not allocate dsyscall_struct");
+ goto out;
+ }
+
+ ds->args = args;
+ ds->func = func;
+ ds->id = dsyscall_next_id++;
+ ds->iteration = dsyscall_iterations;
+ if (!dsyscall_next_id) {
+ if (!++dsyscall_iterations) {
+ printk(KERN_WARNING "dsyscall: iterations has overflowed???");
+ }
+ }
+ strcpy(ds->name,name);
+
+ list_add(&ds->next,&dsyscall_syscalls);
+ dsyscall_hash_add(ds);
+
+ ret = 0;
+ out:
+ up_write(&dsyscall_sem);
+ return ret;
+}
+
+int dsyscall_unregister(char *name)
+{
+ struct dsyscall_struct *ds;
+
+ down_write(&dsyscall_sem);
+ if ((ds = find_dsyscall_by_name(name)) == NULL) {
+ printk("dsyscall: name %s is not registered\n",name);
+ up_write(&dsyscall_sem);
+ return -1;
+ }
+
+ list_del(&ds->next);
+ list_del(&ds->link);
+
+ up_write(&dsyscall_sem);
+
+ kmem_cache_free(dsyscall_cache,ds);
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(dsyscall_register);
+EXPORT_SYMBOL_GPL(dsyscall_unregister);
+
+int __init dsyscall_init(void)
+{
+ int i;
+
+ dsyscall_cache = kmem_cache_create("dsyscalls", sizeof(struct dsyscall_struct),
+ 0, 0, NULL, NULL);
+ if (!dsyscall_cache)
+ panic ("Can't allocate dsyscall cache!"); /* Too much? something else is wrong if
+ we fail here. */
+
+ for (i=0; i < DSYSCALL_HASH_SIZE; i++) {
+ INIT_LIST_HEAD(&dsyscall_hash[i]);
+ }
+
+ return 0;
+}
+__initcall(dsyscall_init);
Index: include/asm-i386/unistd.h
===================================================================
--- include/asm-i386/unistd.h (revision 15)
+++ include/asm-i386/unistd.h (working copy)
@@ -300,8 +300,9 @@
#define __NR_vperfctr_unlink (__NR_perfctr_info+3)
#define __NR_vperfctr_iresume (__NR_perfctr_info+4)
#define __NR_vperfctr_read (__NR_perfctr_info+5)
+#define __NR_dsyscall 295

-#define NR_syscalls 295
+#define NR_syscalls 296

/*
* user-visible error numbers are in the range -1 - -128: see
Index: include/linux/dsyscall.h
===================================================================
--- include/linux/dsyscall.h (revision 0)
+++ include/linux/dsyscall.h (revision 0)
@@ -0,0 +1,174 @@
+/*
+ * dsyscall.h
+ *
+ * Copyright (C) 2004 Steven Rostedt <steven.rostedt@xxxxxxxxxxxxx>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#ifndef _LINUX_DSYSCALL_H
+#define _LINUX_DSYSCALL_H
+
+#define DSYSCALL_NAME_SZ 128
+#define DSYSCALL_MAX_ARGS 6
+
+enum dsyscall_cmd {
+ DSYSCALL_GET = 1,
+ DSYSCALL_CALL,
+};
+
+struct dsyscall {
+ char name[DSYSCALL_NAME_SZ]; /* Name of system call */
+ long id; /* unique id of available dynamic syscalls */
+ long iteration; /* incase this box runs longer than we have been on earth
+ count the iterations of id's. If interation overflows,
+ then we are simply out of luck! */
+ int argc; /* Number of arguments that this system call takes. */
+ long *argv; /* pointer to the argument list */
+};
+
+#define DSYSCALL_FUNC(func) ((int(*)(long,...))func)
+
+#ifdef __KERNEL__
+
+#include <linux/list.h>
+
+struct dsyscall_struct {
+ struct list_head next; /* list of all dynamic syscalls */
+ struct list_head link; /* used in the hash */
+ char name[DSYSCALL_NAME_SZ]; /* name of the dynamic syscall */
+ int args; /* number of arguments for this syscall */
+ int (*func)(long,...); /* The actual dynamic system call */
+ long id;
+ long iteration;
+
+};
+
+int dsyscall_register(char *name, int args, int (*func)(long,...));
+int dsyscall_unregister(char *name);
+
+#else /* !__KERNEL__ */
+
+/* OK, this should probably be in another file for users */
+#include <asm/unistd.h>
+
+#define _dsyscall_call(name,args,arg) \
+{ \
+ long __res; \
+ static int has_init = 0; \
+ static struct dsyscall ds; \
+ if (!has_init) { \
+ __res = dsyscall(DSYSCALL_GET,#name,&ds); \
+ if (!__res) \
+ has_init = 1; \
+ else \
+ return __res; \
+ } \
+ ds.argc = args; \
+ ds.argv = arg; \
+ __res = dsyscall(DSYSCALL_CALL,#name,&ds); \
+ if (__res < 0 && errno == ENODEV) { \
+ has_init = 0; \
+ } \
+ return __res; \
+}
+
+#define _dsyscall0(type,name) \
+type name(void) \
+{ \
+ long argv[0]; \
+ _dsyscall_call(name,0,argv); \
+ return 0; /* not reached */ \
+}
+
+#define _dsyscall1(type,name,type1,arg1) \
+type name(type1 arg1) \
+{ \
+ long argv[1]; \
+ argv[0] = (long)arg1; \
+ _dsyscall_call(name,1,argv); \
+ return 0; /* not reached */ \
+}
+
+#define _dsyscall2(type,name,type1,arg1,type2,arg2) \
+type name(type1 arg1, type2 arg2) \
+{ \
+ long argv[2]; \
+ argv[0] = (long)arg1; \
+ argv[1] = (long)arg2; \
+ _dsyscall_call(name,2,argv); \
+ return 0; /* not reached */ \
+}
+
+#define _dsyscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+type name(type1 arg1, type2 arg2, type3 arg3) \
+{ \
+ long argv[3]; \
+ argv[0] = (long)arg1; \
+ argv[1] = (long)arg2; \
+ argv[2] = (long)arg3; \
+ _dsyscall_call(name,3,argv); \
+ return 0; /* not reached */ \
+}
+
+#define _dsyscall4(type,name,type1,arg1,type2,arg2,type3,arg3, \
+ type4,arg4) \
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+{ \
+ long argv[4]; \
+ argv[0] = (long)arg1; \
+ argv[1] = (long)arg2; \
+ argv[2] = (long)arg3; \
+ argv[3] = (long)arg4; \
+ _dsyscall_call(name,4,argv); \
+ return 0; /* not reached */ \
+}
+
+#define _dsyscall5(type,name,type1,arg1,type2,arg2,type3,arg3, \
+ type4,arg4,type5,arg5) \
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) \
+{ \
+ long argv[5]; \
+ argv[0] = (long)arg1; \
+ argv[1] = (long)arg2; \
+ argv[2] = (long)arg3; \
+ argv[3] = (long)arg4; \
+ argv[4] = (long)arg5; \
+ _dsyscall_call(name,5,argv); \
+ return 0; /* not reached */ \
+}
+
+#define _dsyscall6(type,name,type1,arg1,type2,arg2,type3,arg3, \
+ type4,arg4,type5,arg5,type6,arg6) \
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5, type6 arg6) \
+{ \
+ long argv[6]; \
+ argv[0] = (long)arg1; \
+ argv[1] = (long)arg2; \
+ argv[2] = (long)arg3; \
+ argv[3] = (long)arg4; \
+ argv[4] = (long)arg5; \
+ argv[5] = (long)arg6; \
+ _dsyscall_call(name,6,argv); \
+ return 0; /* not reached */ \
+}
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_DSYSCALL_H */
Index: init/Kconfig
===================================================================
--- init/Kconfig (revision 15)
+++ init/Kconfig (working copy)
@@ -249,6 +249,14 @@
through /proc/config.gz.


+config DSYSCALL
+ bool "Enable dynamic system calls"
+ default y
+ help
+ This option enables usage of dynamic system calls by drivers.
+ This allows drivers to register system calls that are not
+ already defined by the compiled kernel.
+
menuconfig EMBEDDED
bool "Configure standard kernel features (for small systems)"
help
Index: arch/i386/kernel/entry.S
===================================================================
--- arch/i386/kernel/entry.S (revision 15)
+++ arch/i386/kernel/entry.S (working copy)
@@ -906,5 +906,10 @@
.long sys_vperfctr_unlink
.long sys_vperfctr_iresume
.long sys_vperfctr_read
+#ifdef CONFIG_DSYSCALL
+ .long sys_dsyscall /* 295 */
+#else
+ .long sys_ni_syscall /* 295 */
+#endif

syscall_table_size=(.-sys_call_table)

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/