public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] typesafe callbacks
@ 2008-07-25  2:13 Rusty Russell
  2008-07-25  2:14 ` [PATCH 1/4] typesafe: cast_if_type: allow macros functions which take more than one type Rusty Russell
  0 siblings, 1 reply; 5+ messages in thread
From: Rusty Russell @ 2008-07-25  2:13 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel, Al Viro

It's possible to portably make callbacks typesafe, as well as taking the 
existing void * arg.  This makes the callers more readable and more typesafe.

Done in a way that doesn't break non-gcc: they just don't get a warning.

Been in linux-next for about two full cycles now, and I really want to start 
using this in my own code.

Thanks,
Rusty.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/4] typesafe: cast_if_type: allow macros functions which take more than one type.
  2008-07-25  2:13 [PATCH 0/4] typesafe callbacks Rusty Russell
@ 2008-07-25  2:14 ` Rusty Russell
  2008-07-25  2:16   ` [PATCH 2/4] typesafe: typesafe_cb: wrappers for typesafe callbacks Rusty Russell
  0 siblings, 1 reply; 5+ messages in thread
From: Rusty Russell @ 2008-07-25  2:14 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel, Al Viro, Harvey Harrison

To create functions which can take two types, but still warn on any
other types, we need a way of casting one type and no others.

To make things more complex, it should correctly handle function args,
NULL, and be usable in initializers.  __builtin_choose_expr was
introduced in gcc 3.1 (kernel needs >= 3.2 anyway).

(includes Harvey Harrison <harvey.harrison@gmail.com>'s sparse fix)

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Cc: Harvey Harrison <harvey.harrison@gmail.com>
---
 include/linux/compiler-gcc.h   |   18 ++++++++++++++++++
 include/linux/compiler-intel.h |    2 ++
 2 files changed, 20 insertions(+)

diff -r ecbc022961d8 include/linux/compiler-gcc.h
--- a/include/linux/compiler-gcc.h	Mon Jul 14 12:30:35 2008 +1000
+++ b/include/linux/compiler-gcc.h	Mon Jul 14 20:46:23 2008 +1000
@@ -61,3 +61,21 @@
 #define  noinline			__attribute__((noinline))
 #define __attribute_const__		__attribute__((__const__))
 #define __maybe_unused			__attribute__((unused))
+
+/**
+ * cast_if_type - allow an alternate type
+ * @expr: the expression to optionally cast
+ * @oktype: the type to allow.
+ * @desttype: the type to cast to.
+ *
+ * This is used to accept a particular alternate type for an expression:
+ * because any other types will not be cast, they will cause a warning as
+ * normal.
+ *
+ * Note that the unnecessary trinary forces functions to devolve into
+ * function pointers as users expect, but means @expr must be a pointer or
+ * integer.
+ */
+#define cast_if_type(expr, oktype, desttype)	__builtin_choose_expr(	\
+	__builtin_types_compatible_p(typeof(1?(expr):(expr)), oktype),	\
+	(desttype)(expr), (expr))
diff -r ecbc022961d8 include/linux/compiler-intel.h
--- a/include/linux/compiler-intel.h	Mon Jul 14 12:30:35 2008 +1000
+++ b/include/linux/compiler-intel.h	Mon Jul 14 20:46:23 2008 +1000
@@ -29,3 +29,5 @@
 #endif
 
 #define uninitialized_var(x) x
+
+#define cast_if_type(expr, oktype, desttype) ((desttype)(expr))

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/4] typesafe: typesafe_cb: wrappers for typesafe callbacks.
  2008-07-25  2:14 ` [PATCH 1/4] typesafe: cast_if_type: allow macros functions which take more than one type Rusty Russell
@ 2008-07-25  2:16   ` Rusty Russell
  2008-07-25  2:17     ` [PATCH 3/4] typesafe: kthread_create and kthread_run Rusty Russell
  0 siblings, 1 reply; 5+ messages in thread
From: Rusty Russell @ 2008-07-25  2:16 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel, Al Viro, Harvey Harrison

cast_if_type is clever but annoying to use, so we make some slightly
less annoying wrappers for callback registration.

These allow const pointer-taking functions as well as the normal
non-const ones (volatile variant omitted for simplicity).  We also
have a typesafe_cb_preargs() for callbacks which don't just take one
argument.

Here's an example of use:

BEFORE:
	void foo_set_callback(int (*callback)(void *), void *arg);

AFTER:
	#define foo_set_callback(cb, arg) \
		__foo_set_callback(typesafe_cb(int, (cb), (arg)), (arg))
	void __foo_set_callback(int (*callback)(void *), void *arg);

o If cb is of form "int cb(typeof(arg))" or "int cb(const typeof(arg))" it
  will be cast to form "int cb(void *)".
o Otherwise, if cb is not already of form "int cb(void *)" it will cause a
  warning when passed to __foo_set_callback().
o If arg is not a pointer, then handing it to __foo_set_callback() will cause
  a warning.

Note: there are at least two possible additions we don't yet need.  We
don't cover callbacks which take a volatile pointer, even though that
would be fine, and we don't have a typesafe_cb_postargs().

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 include/linux/kernel.h |   35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff -r 907c55be656f include/linux/kernel.h
--- a/include/linux/kernel.h	Mon Apr 21 06:58:00 2008 +1000
+++ b/include/linux/kernel.h	Mon Apr 21 07:04:02 2008 +1000
@@ -436,4 +436,39 @@ struct sysinfo {
 #define NUMA_BUILD 0
 #endif
 
+/* If fn is of type ok1 or ok2, cast to desttype */
+#define __typesafe_cb(desttype, fn, ok1, ok2) \
+	cast_if_type(cast_if_type((fn), ok1, desttype), ok2, desttype)
+
+/**
+ * typesafe_cb - cast a callback function if it matches the arg
+ * @rettype: the return type of the callback function
+ * @fn: the callback function to cast
+ * @arg: the (pointer) argument to hand to the callback function.
+ *
+ * If a callback function takes a single argument, this macro does
+ * appropriate casts to a function which takes a single void * argument if the
+ * callback provided matches the @arg (or a const or volatile version).
+ *
+ * It is assumed that @arg is of pointer type: usually @arg is passed
+ * or assigned to a void * elsewhere anyway.
+ */
+#define typesafe_cb(rettype, fn, arg)					\
+	__typesafe_cb(rettype (*)(void *), (fn),			\
+		      rettype (*)(const typeof(arg)),			\
+		      rettype (*)(typeof(arg)))
+
+/**
+ * typesafe_cb_preargs - cast a callback function if it matches the arg
+ * @rettype: the return type of the callback function
+ * @fn: the callback function to cast
+ * @arg: the (pointer) argument to hand to the callback function.
+ *
+ * This is a version of typesafe_cb() for callbacks that take other arguments
+ * before the @arg.
+ */
+#define typesafe_cb_preargs(rettype, fn, arg, ...)			\
+	__typesafe_cb(rettype (*)(__VA_ARGS__, void *), (fn),		\
+		      rettype (*)(__VA_ARGS__, const typeof(arg)),	\
+		      rettype (*)(__VA_ARGS__, typeof(arg)))
 #endif

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 3/4] typesafe: kthread_create and kthread_run
  2008-07-25  2:16   ` [PATCH 2/4] typesafe: typesafe_cb: wrappers for typesafe callbacks Rusty Russell
@ 2008-07-25  2:17     ` Rusty Russell
  2008-07-25  2:19       ` [PATCH 4/4] typesafe: TIMER_INITIALIZER and setup_timer Rusty Russell
  0 siblings, 1 reply; 5+ messages in thread
From: Rusty Russell @ 2008-07-25  2:17 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel, Al Viro, Harvey Harrison

(Assumes patch to add printf attr to kthread_create is applied, but fixup trivial)

Straight-forward conversion to allow typesafe callbacks.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 include/linux/kthread.h |   28 +++++++++++++++++++++++++---
 kernel/kthread.c        |   29 +++++------------------------
 2 files changed, 30 insertions(+), 27 deletions(-)

diff -r f5a7f7f9683c include/linux/kthread.h
--- a/include/linux/kthread.h	Mon Apr 07 18:36:20 2008 +1000
+++ b/include/linux/kthread.h	Mon Apr 07 19:12:04 2008 +1000
@@ -4,9 +4,31 @@
 #include <linux/err.h>
 #include <linux/sched.h>
 
-struct task_struct *kthread_create(int (*threadfn)(void *data),
-				   void *data,
-				   const char namefmt[], ...)
+/**
+ * kthread_create - create a kthread.
+ * @threadfn: the function to run until signal_pending(current).
+ * @data: data ptr for @threadfn.
+ * @namefmt: printf-style name for the thread.
+ *
+ * Description: This helper function creates and names a kernel
+ * thread.  The thread will be stopped: use wake_up_process() to start
+ * it.  See also kthread_run(), kthread_create_on_cpu().
+ *
+ * When woken, the thread will run @threadfn() with @data as its
+ * argument. @threadfn() can either call do_exit() directly if it is a
+ * standalone thread for which noone will call kthread_stop(), or
+ * return when 'kthread_should_stop()' is true (which means
+ * kthread_stop() has been called).  The return value should be zero
+ * or a negative error number; it will be passed to kthread_stop().
+ *
+ * Returns a task_struct or ERR_PTR(-ENOMEM).
+ */
+#define kthread_create(threadfn, data, namefmt...)			\
+	__kthread_create(typesafe_cb(int,(threadfn),(data)), (data), namefmt)
+
+struct task_struct *__kthread_create(int (*threadfn)(void *data),
+				     void *data,
+				     const char namefmt[], ...)
 	__attribute__((format(printf, 3, 4)));
 
 /**
diff -r f5a7f7f9683c kernel/kthread.c
--- a/kernel/kthread.c	Mon Apr 07 18:36:20 2008 +1000
+++ b/kernel/kthread.c	Mon Apr 07 19:12:04 2008 +1000
@@ -112,29 +112,10 @@ static void create_kthread(struct kthrea
 	complete(&create->done);
 }
 
-/**
- * kthread_create - create a kthread.
- * @threadfn: the function to run until signal_pending(current).
- * @data: data ptr for @threadfn.
- * @namefmt: printf-style name for the thread.
- *
- * Description: This helper function creates and names a kernel
- * thread.  The thread will be stopped: use wake_up_process() to start
- * it.  See also kthread_run(), kthread_create_on_cpu().
- *
- * When woken, the thread will run @threadfn() with @data as its
- * argument. @threadfn() can either call do_exit() directly if it is a
- * standalone thread for which noone will call kthread_stop(), or
- * return when 'kthread_should_stop()' is true (which means
- * kthread_stop() has been called).  The return value should be zero
- * or a negative error number; it will be passed to kthread_stop().
- *
- * Returns a task_struct or ERR_PTR(-ENOMEM).
- */
-struct task_struct *kthread_create(int (*threadfn)(void *data),
-				   void *data,
-				   const char namefmt[],
-				   ...)
+struct task_struct *__kthread_create(int (*threadfn)(void *data),
+				     void *data,
+				     const char namefmt[],
+				     ...)
 {
 	struct kthread_create_info create;
 
@@ -159,7 +140,7 @@ struct task_struct *kthread_create(int (
 	}
 	return create.result;
 }
-EXPORT_SYMBOL(kthread_create);
+EXPORT_SYMBOL(__kthread_create);
 
 /**
  * kthread_bind - bind a just-created kthread to a cpu.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 4/4] typesafe: TIMER_INITIALIZER and setup_timer
  2008-07-25  2:17     ` [PATCH 3/4] typesafe: kthread_create and kthread_run Rusty Russell
@ 2008-07-25  2:19       ` Rusty Russell
  0 siblings, 0 replies; 5+ messages in thread
From: Rusty Russell @ 2008-07-25  2:19 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel, Al Viro, Harvey Harrison

This patch lets timer callback functions have their natural type
(ie. exactly match the data pointer type); it allows the old "unsigned
long data" type as well.

Downside: if you use the old "unsigned long" callback type, you won't
get a warning if your data is not an unsigned long, due to the cast.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 include/linux/timer.h |   32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff -r 138795de70da include/linux/timer.h
--- a/include/linux/timer.h	Thu May 01 21:03:51 2008 +1000
+++ b/include/linux/timer.h	Thu May 01 21:05:13 2008 +1000
@@ -25,12 +25,22 @@ struct timer_list {
 
 extern struct tvec_base boot_tvec_bases;
 
-#define TIMER_INITIALIZER(_function, _expires, _data) {		\
-		.entry = { .prev = TIMER_ENTRY_STATIC },	\
-		.function = (_function),			\
-		.expires = (_expires),				\
-		.data = (_data),				\
-		.base = &boot_tvec_bases,			\
+/*
+ * For historic reasons the timer function takes an unsigned long, so
+ * we use this variant of typesafe_cb.  data is converted to an unsigned long
+ * if it is another integer type, by adding 0UL.
+ */
+#define typesafe_timerfn(fn, data)				\
+	__typesafe_cb(void (*)(unsigned long), (fn),		\
+		      void (*)(const typeof((data)+0UL)),	\
+		      void (*)(typeof((data)+0UL)))
+
+#define TIMER_INITIALIZER(_function, _expires, _data) {			\
+		.entry = { .prev = TIMER_ENTRY_STATIC },		\
+		.function = typesafe_timerfn((_function), (_data)),	\
+		.expires = (_expires),					\
+		.data = (unsigned long)(_data),				\
+		.base = &boot_tvec_bases,				\
 	}
 
 #define DEFINE_TIMER(_name, _function, _expires, _data)		\
@@ -51,9 +61,13 @@ static inline void init_timer_on_stack(s
 }
 #endif
 
-static inline void setup_timer(struct timer_list * timer,
-				void (*function)(unsigned long),
-				unsigned long data)
+#define setup_timer(timer, function, data)				\
+	__setup_timer((timer), typesafe_timerfn((function), (data)),	\
+		      (unsigned long)(data))
+
+static inline void __setup_timer(struct timer_list *timer,
+				 void (*function)(unsigned long),
+				 unsigned long data)
 {
 	timer->function = function;
 	timer->data = data;

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2008-07-25  2:19 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-25  2:13 [PATCH 0/4] typesafe callbacks Rusty Russell
2008-07-25  2:14 ` [PATCH 1/4] typesafe: cast_if_type: allow macros functions which take more than one type Rusty Russell
2008-07-25  2:16   ` [PATCH 2/4] typesafe: typesafe_cb: wrappers for typesafe callbacks Rusty Russell
2008-07-25  2:17     ` [PATCH 3/4] typesafe: kthread_create and kthread_run Rusty Russell
2008-07-25  2:19       ` [PATCH 4/4] typesafe: TIMER_INITIALIZER and setup_timer Rusty Russell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox