[ovs-dev] [PATCH] datapath: Use "make" instead of Autoconf to figure out kernel compat issues.

Ben Pfaff blp at nicira.com
Mon Mar 5 17:32:12 UTC 2012


This has a few advantages:

  * It eliminates the need to infer the kernel source directory based on
    the build directory, which has broken several times during Open vSwitch
    development.

  * It eliminates most of the kernel-related Autoconf code, which should
    make it easier to in turn eliminate the openvswitch-datapath-dkms
    dependency on libc6-dev.  (Currently, openvswitch-datapath-dkms has to
    depend on libc6-dev because "configure" fails hard if the C compiler
    cannot generate executables, even though the dkms package never does
    that.)

  * Related to the previous point, it would make it easier to distribute
    a tarball that builds only the kernel module and does not contain any
    of the userspace code.  (This may not be a worthwhile goal, but it
    makes sense to only distribute kernel module code in the Debian
    packages that build kernel modules.)

  * If the kernel headers change (e.g. someone points a symlink to a new
    kernel tree), then everything works OK without a new "configure" step.

It has some disadvantages:

  * This is a new technique with which I have no experience, and so it
    might have new and unexpected pitfalls.

  * The makefile syntax that replaces the Autoconf syntax might actually
    be harder to read.

Signed-off-by: Ben Pfaff <blp at nicira.com>
---
Something fun for you to look at now that you're back.

Originally posted here, with incomplete changelog:
http://openvswitch.org/pipermail/dev/2012-February/015083.html

 acinclude.m4                    |  206 +--------------------------------------
 datapath/linux/.gitignore       |    2 +-
 datapath/linux/Kbuild.in        |  120 ++++++++++++++++++++++-
 datapath/linux/Makefile.main.in |    3 +-
 4 files changed, 122 insertions(+), 209 deletions(-)

diff --git a/acinclude.m4 b/acinclude.m4
index 69bb772..d1347ef 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -32,15 +32,9 @@ AC_DEFUN([OVS_CHECK_LINUX], [
   AC_ARG_WITH([linux],
               [AC_HELP_STRING([--with-linux=/path/to/linux],
                               [Specify the Linux kernel build directory])])
-  AC_ARG_WITH([linux-source],
-              [AC_HELP_STRING([--with-linux-source=/path/to/linux-source],
-                              [Specify the Linux kernel source directory
-			       (usually figured out automatically from build
-			       directory)])])
 
-  # Deprecated equivalents to --with-linux, --with-linux-source.
+  # Deprecated equivalent to --with-linux.
   AC_ARG_WITH([l26])
-  AC_ARG_WITH([l26-source])
 
   if test X"$with_linux" != X; then
     KBUILD=$with_linux
@@ -51,19 +45,6 @@ AC_DEFUN([OVS_CHECK_LINUX], [
     KBUILD=
   fi
 
-  if test X"$KBUILD" != X; then
-    if test X"$with_linux_source" != X; then
-      KSRC=$with_linux_source
-    elif test X"$with_l26_source" != X; then
-      KSRC=$with_l26_source
-      AC_MSG_WARN([--with-l26-source is deprecated, please use --with-linux-source instead])
-    else
-      KSRC=
-    fi
-  elif test X"$with_linux_source" != X || test X"$with_l26_source" != X; then
-    AC_MSG_ERROR([Linux source directory may not be specified without Linux build directory])
-  fi
-
   if test -n "$KBUILD"; then
     KBUILD=`eval echo "$KBUILD"`
     case $KBUILD in
@@ -82,200 +63,15 @@ AC_DEFUN([OVS_CHECK_LINUX], [
 	AC_ERROR([source dir $KBUILD doesn't exist])
     fi
 
-    # Debian breaks kernel headers into "source" header and "build" headers.
-    # We want the source headers, but $KBUILD gives us the "build" headers.
-    # Use heuristics to find the source headers.
-    AC_MSG_CHECKING([for Linux source directory])
-    if test -n "$KSRC"; then
-      KSRC=`eval echo "$KSRC"`
-      case $KSRC in
-          /*) ;;
-          *) KSRC=`pwd`/$KSRC ;;
-      esac
-      if test ! -e $KSRC/include/linux/kernel.h; then
-        AC_MSG_ERROR([$KSRC is not a kernel source directory])
-      fi
-    else
-      KSRC=$KBUILD
-      if test ! -e $KSRC/include/linux/kernel.h; then
-        # Debian kernel build Makefiles tend to include a line of the form:
-        # MAKEARGS := -C /usr/src/linux-headers-3.2.0-1-common O=/usr/src/linux-headers-3.2.0-1-486
-        # First try to extract the source directory from this line.
-        KSRC=`sed -n 's/.*-C \([[^ ]]*\).*/\1/p' "$KBUILD"/Makefile`
-        if test ! -e "$KSRC"/include/linux/kernel.h; then
-          # Didn't work.  Fall back to name-based heuristics that used to work.
-          case `echo "$KBUILD" | sed 's,/*$,,'` in # (
-            */build)
-              KSRC=`echo "$KBUILD" | sed 's,/build/*$,/source,'`
-              ;; # (
-            *)
-              KSRC=`(cd $KBUILD && pwd -P) | sed 's,-[[^-]]*$,-common,'`
-              ;;
-          esac
-        fi
-      fi
-      if test ! -e "$KSRC"/include/linux/kernel.h; then
-        AC_MSG_ERROR([cannot find source directory (please use --with-linux-source)])
-      fi
-    fi
-    AC_MSG_RESULT([$KSRC])
-
-    AC_MSG_CHECKING([for kernel version])
-    version=`sed -n 's/^VERSION = //p' "$KSRC/Makefile"`
-    patchlevel=`sed -n 's/^PATCHLEVEL = //p' "$KSRC/Makefile"`
-    sublevel=`sed -n 's/^SUBLEVEL = //p' "$KSRC/Makefile"`
-    if test X"$version" = X || test X"$patchlevel" = X; then
-       AC_ERROR([cannot determine kernel version])
-    elif test X"$sublevel" = X; then
-       kversion=$version.$patchlevel
-    else
-       kversion=$version.$patchlevel.$sublevel
-    fi
-    AC_MSG_RESULT([$kversion])
-
-    if test "$version" -ge 3; then
-       : # Linux 3.x
-    elif test "$version" = 2 && test "$patchlevel" -ge 6; then
-       : # Linux 2.6.x
-    else
-       if test "$KBUILD" = "$KSRC"; then
-         AC_ERROR([Linux kernel in $KBUILD is version $kversion, but version 2.6 or later is required])
-       else
-         AC_ERROR([Linux kernel in build tree $KBUILD (source tree $KSRC) is version $kversion, but version 2.6 or later is required])
-       fi
-    fi
     if test ! -e "$KBUILD"/include/linux/version.h || \
        (test ! -e "$KBUILD"/include/linux/autoconf.h && \
         test ! -e "$KBUILD"/include/generated/autoconf.h); then
 	AC_MSG_ERROR([Linux kernel source in $KBUILD is not configured])
     fi
-    OVS_CHECK_LINUX_COMPAT
   fi
   AM_CONDITIONAL(LINUX_ENABLED, test -n "$KBUILD")
 ])
 
-dnl OVS_GREP_IFELSE(FILE, REGEX, [IF-MATCH], [IF-NO-MATCH])
-dnl
-dnl Greps FILE for REGEX.  If it matches, runs IF-MATCH, otherwise IF-NO-MATCH.
-dnl If IF-MATCH is empty then it defines to OVS_DEFINE(HAVE_<REGEX>), with
-dnl <REGEX> translated to uppercase.
-AC_DEFUN([OVS_GREP_IFELSE], [
-  AC_MSG_CHECKING([whether $2 matches in $1])
-  if test -f $1; then
-    grep '$2' $1 >/dev/null 2>&1
-    status=$?
-    case $status in
-      0) 
-        AC_MSG_RESULT([yes])
-        m4_if([$3], [], [OVS_DEFINE([HAVE_]m4_toupper([$2]))], [$3])
-        ;;
-      1) 
-        AC_MSG_RESULT([no])
-        $4
-        ;;
-      *) 
-        AC_MSG_ERROR([grep exited with status $status])
-        ;;
-    esac
-  else
-    AC_MSG_RESULT([file not found])
-    $4
-  fi
-])
-
-dnl OVS_DEFINE(NAME)
-dnl
-dnl Defines NAME to 1 in kcompat.h.
-AC_DEFUN([OVS_DEFINE], [
-  echo '#define $1 1' >> datapath/linux/kcompat.h.new
-])
-
-AC_DEFUN([OVS_CHECK_LOG2_H], [
-  AC_MSG_CHECKING([for $KSRC/include/linux/log2.h])
-  if test -e $KSRC/include/linux/log2.h; then
-    AC_MSG_RESULT([yes])
-    OVS_DEFINE([HAVE_LOG2_H])
-  else
-    AC_MSG_RESULT([no])
-  fi
-])
-
-dnl OVS_CHECK_LINUX_COMPAT
-dnl
-dnl Runs various Autoconf checks on the Linux 2.6 kernel source in
-dnl the directory in $KBUILD.
-AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
-  rm -f datapath/linux/kcompat.h.new
-  mkdir -p datapath/linux
-  : > datapath/linux/kcompat.h.new
-
-  OVS_GREP_IFELSE([$KSRC/arch/x86/include/asm/checksum_32.h], [src_err,],
-                  [OVS_DEFINE([HAVE_CSUM_COPY_DBG])])
-
-  OVS_GREP_IFELSE([$KSRC/include/linux/err.h], [ERR_CAST])
-
-  OVS_GREP_IFELSE([$KSRC/include/linux/in.h], [ipv4_is_multicast])
-
-  OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_disable_lro])
-  OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_get_stats])
-  OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_get_by_index_rcu])
-
-  OVS_GREP_IFELSE([$KSRC/include/linux/rcupdate.h], [rcu_read_lock_held], [],
-                  [OVS_GREP_IFELSE([$KSRC/include/linux/rtnetlink.h],
-                                   [rcu_read_lock_held])])
-  
-  # Check for the proto_data_valid member in struct sk_buff.  The [^@]
-  # is necessary because some versions of this header remove the
-  # member but retain the kerneldoc comment that describes it (which
-  # starts with @).  The brackets must be doubled because of m4
-  # quoting rules.
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [[[^@]]proto_data_valid],
-                  [OVS_DEFINE([HAVE_PROTO_DATA_VALID])])
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [raw],
-                  [OVS_DEFINE([HAVE_MAC_RAW])])
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_dst(],
-                  [OVS_DEFINE([HAVE_SKB_DST_ACCESSOR_FUNCS])])
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], 
-                  [skb_copy_from_linear_data_offset])
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h],
-                  [skb_reset_tail_pointer])
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_cow_head])
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_transport_header],
-                  [OVS_DEFINE([HAVE_SKBUFF_HEADER_HELPERS])])
-  OVS_GREP_IFELSE([$KSRC/include/linux/icmpv6.h], [icmp6_hdr],
-                  [OVS_DEFINE([HAVE_ICMP6_HDR])])
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_warn_if_lro],
-                  [OVS_DEFINE([HAVE_SKB_WARN_LRO])])
-  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [consume_skb])
-
-  OVS_GREP_IFELSE([$KSRC/include/linux/string.h], [kmemdup], [],
-                  [OVS_GREP_IFELSE([$KSRC/include/linux/slab.h], [kmemdup])])
-
-  OVS_GREP_IFELSE([$KSRC/include/linux/types.h], [bool],
-                  [OVS_DEFINE([HAVE_BOOL_TYPE])])
-  OVS_GREP_IFELSE([$KSRC/include/linux/types.h], [__wsum],
-                  [OVS_DEFINE([HAVE_CSUM_TYPES])])
-
-  OVS_GREP_IFELSE([$KSRC/include/net/checksum.h], [csum_replace4])
-  OVS_GREP_IFELSE([$KSRC/include/net/checksum.h], [csum_unfold])
-
-  OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [NLA_NUL_STRING])
-  OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_get_be16])
-  OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_find_nested])
-
-  OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [ADD_ALL_VLANS_CMD],
-                  [OVS_DEFINE([HAVE_VLAN_BUG_WORKAROUND])])
-
-  OVS_CHECK_LOG2_H
-
-  if cmp -s datapath/linux/kcompat.h.new \
-            datapath/linux/kcompat.h >/dev/null 2>&1; then
-    rm datapath/linux/kcompat.h.new
-  else
-    mv datapath/linux/kcompat.h.new datapath/linux/kcompat.h
-  fi
-])
-
 dnl Checks for net/if_packet.h.
 AC_DEFUN([OVS_CHECK_IF_PACKET],
   [AC_CHECK_HEADER([net/if_packet.h],
diff --git a/datapath/linux/.gitignore b/datapath/linux/.gitignore
index 0aee746..8067279 100644
--- a/datapath/linux/.gitignore
+++ b/datapath/linux/.gitignore
@@ -19,7 +19,7 @@
 /genetlink-brcompat.c
 /genetlink-openvswitch.c
 /ip_output-openvswitch.c
-/kcompat.h
+/kcompat/
 /kmemdup.c
 /loop_counter.c
 /modules.order
diff --git a/datapath/linux/Kbuild.in b/datapath/linux/Kbuild.in
index 07d106d..a451f41 100644
--- a/datapath/linux/Kbuild.in
+++ b/datapath/linux/Kbuild.in
@@ -17,7 +17,7 @@ else
 EXTRA_CFLAGS += -DBUILDNR=\"+build$(BUILDNR)\"
 endif
 EXTRA_CFLAGS += -g
-EXTRA_CFLAGS += -include $(builddir)/kcompat.h
+EXTRA_CFLAGS += -include $(builddir)/kcompat/kcompat.h
 
 # These include directories have to go before -I$(KSRC)/include.
 # NOSTDINC_FLAGS just happens to be a variable that goes in the
@@ -28,6 +28,124 @@ obj-m := $(patsubst %,%_mod.o,$(build_modules))
 
 define module_template
 $(1)_mod-y = $$(notdir $$(patsubst %.c,%.o,$($(1)_sources)))
+$$(addprefix $$(obj)/,$$(notdir $$(patsubst %.c,%.o,$($(1)_sources)))): $(obj)/kcompat/kcompat.h
 endef
+#$(addprefix $(obj)/,$(notdir $(patsubst %.c,%.o,$(brcompat_sources)))): $(obj)/kcompat/kcompat.h
 
 $(foreach module,$(build_modules),$(eval $(call module_template,$(module))))
+
+define GREP
+for file in $^; do \
+    if test -f "$$file"; then \
+        grep '$(1)' $$file >/dev/null 2>&1; \
+	status=$$?; \
+	case $$status in \
+	  0) \
+            $(kecho) "  GREP   \"$(1)\" found in $$file"; \
+	    echo >&3 '#define $(if $(2),$(2),HAVE_$(shell echo $(1) | tr a-z A-Z)) 1'; \
+	    break \
+	    ;; \
+	  1) \
+            $(kecho) "  GREP   \"$(1)\" not in $$file"; \
+	    ;; \
+	  *) \
+	    echo "grep exited with status $$status"; \
+	    exit $$status \
+	    ;; \
+	esac; \
+    else \
+        $(kecho) "file not found"; \
+    fi; \
+done
+endef
+
+$(obj)/datapath.o: $(obj)/kcompat/kcompat.h
+$(obj)/kcompat/kcompat.h: \
+    $(obj)/kcompat/checksum.h \
+    $(obj)/kcompat/err.h \
+    $(obj)/kcompat/icmpv6.h \
+    $(obj)/kcompat/if_vlan.h \
+    $(obj)/kcompat/in.h \
+    $(obj)/kcompat/netdevice.h \
+    $(obj)/kcompat/netlink.h \
+    $(obj)/kcompat/rcupdate.h \
+    $(obj)/kcompat/skbuff.h \
+    $(obj)/kcompat/string.h \
+    $(obj)/kcompat/types.h \
+    $(obj)/kcompat/checksum_32.h \
+    $(obj)/kcompat/log2.h
+	@$(kecho) "  GEN     $@"
+	@$(O)cat $^ > $@.$$$$ && mv $@.$$$$ $@
+
+$(obj)/kcompat/checksum.h: include/net/checksum.h
+	@($(call GREP,csum_replace4); \
+	  $(call GREP,csum_unfold)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/err.h: include/linux/err.h
+	@($(call GREP,ERR_CAST)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/icmpv6.h: include/linux/icmpv6.h
+	@($(call GREP,icmp6_hdr)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/if_vlan.h: include/linux/if_vlan.h
+	@($(call GREP,ADD_ALL_VLANS_CMD,HAVE_VLAN_BUG_WORKAROUND)) \
+	3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/in.h: include/linux/in.h
+	@($(call GREP,ipv4_is_multicast)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/netdevice.h: include/linux/netdevice.h
+	@($(call GREP,dev_disable_lro); \
+	  $(call GREP,dev_get_stats); \
+	  $(call GREP,dev_get_by_index_rcu)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/netlink.h: include/net/netlink.h
+	@($(call GREP,NLA_NUL_STRING); \
+	  $(call GREP,nla_get_be16); \
+	  $(call GREP,nla_find_nested)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/rcupdate.h: include/linux/rcupdate.h include/linux/rtnetlink.h
+	@($(call GREP,rcu_read_lock_held)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/skbuff.h: include/linux/skbuff.h
+	@($(call GREP,[^@]proto_data_valid,HAVE_PROTO_DATA_VALID); \
+	  $(call GREP,raw,HAVE_MAC_RAW); \
+	  ${call GREP,skb_dst(,HAVE_SKB_DST_ACCESSOR_FUNCS}; \
+	  $(call GREP,skb_copy_from_linear_data_offset); \
+	  $(call GREP,skb_reset_tail_pointer); \
+	  $(call GREP,skb_cow_head); \
+	  $(call GREP,skb_transport_header,HAVE_SKBUFF_HEADER_HELPERS); \
+	  $(call GREP,skb_warn_if_lro,HAVE_SKB_WARN_LRO); \
+	  $(call GREP,consume_skb)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/string.h: include/linux/string.h include/linux/slab.h
+	@($(call GREP,kmemdup)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+$(obj)/kcompat/types.h: include/linux/types.h
+	@($(call GREP,bool,HAVE_BOOL_TYPE); \
+	  $(call GREP,__wsum,HAVE_CSUM_TYPES)) 3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+
+ifneq ($(wildcard arch/x86/include/asm/checksum_32.h),)
+# Generate kcompat/checksum_32.h.
+comma := ,
+$(obj)/kcompat/checksum_32.h: arch/x86/include/asm/checksum_32.h
+	@($(call GREP,src_err$(comma),HAVE_CSUM_COPY_DBG)) \
+	3> $@.tmp$$$$; mv $@.tmp$$$$ $@
+else ifneq ($(shell (test ! -e $(obj)/kcompat/checksum_32.h || test -s $(obj)/kcompat/checksum_32.h) && echo needs-update),)
+# kcompat/checksum_32.h needs to be an empty file but isn't.
+.PHONY: $(obj)/kcompat/checksum_32.h
+$(obj)/kcompat/checksum_32.h:
+	: > $@
+else
+# kcompat/checksum_32.h is empty and that's what we want.
+endif
+
+ifneq ($(wildcard include/linux/log2.h),)
+ifneq ($(shell test -s $(obj)/kcompat/log2.h && echo nonempty),)
+# $(obj)/kcompat/log2.h is nonempty and should be.
+else
+# $(obj)/kcompat/log2.h is empty but should not be.
+.PHONY: $(obj)/kcompat/log2.h
+$(obj)/kcompat/log2.h:
+	@echo '#define HAVE_LOG2_H 1' > $@.tmp$$$$ && mv $@.tmp$$$$ $@
+endif
+else
+ifneq ($(shell test -s $(obj)/kcompat/log2.h && echo nonempty),)
+# $(obj)/kcompat/log2.h is nonempty but should be empty.
+.PHONY: $(obj)/kcompat/log2.h
+$(obj)/kcompat/log2.h:
+	: > $@
+else
+# $(obj)/kcompat/log2.h is empty and should be.
+endif
+endif
diff --git a/datapath/linux/Makefile.main.in b/datapath/linux/Makefile.main.in
index 0302fc6..5d5d29e 100644
--- a/datapath/linux/Makefile.main.in
+++ b/datapath/linux/Makefile.main.in
@@ -29,10 +29,9 @@ check: all
 installcheck:
 mostlyclean:
 clean:
-	rm -f *.o *.ko *_mod.* Module.symvers *.cmd kcompat.h.new
+	rm -f *.o *.ko *_mod.* Module.symvers *.cmd kcompat/*.h
 	for d in $(build_links); do if test -h $$d; then rm $$d; fi; done
 distclean: clean
-	rm -f kcompat.h
 maintainer-clean: distclean
 dvi:
 pdf:
-- 
1.7.2.5




More information about the dev mailing list