linux-next に patch 投げた -> 入った

LinuxCon で話した kernel hackers に linux-next を使え、と言われたので linux-next を使おうとしました。とりあえず、いつも通りの make oldconfig

% git clone git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
% cd linux-next
% zcat /proc/config.gz > .config
% make oldconfig

ところが、 oldconfig がうまく動いていないです。なぜか、すでに定義してあるはずの "EXPERIMENTAL" を "NEW" と認識しています。 .config がうまく読まれてないですね。

<snip>
scripts/kconfig/conf --oldconfig Kconfig
#
# using defaults found in /boot/config-2.6.34-ccs-r1
#
*
* Restart config...
*
*
* General setup
*
Prompt for development and/or incomplete code/drivers (EXPERIMENTAL) [N/y/?] (NEW)

とりあえず、適当なとこでテストしてみて git bisect を試みます。

% git checkout v2.6.36-rc6
% make oldconfig # <- 普通に動く
% git bisect start master v2.6.36-rc6

結果、こういうことになった最初の commit は 8f9b217448dfb8acf2b320960247d3139c9744a6 だったとわかります。

% git show
commit 8f9b217448dfb8acf2b320960247d3139c9744a6
Merge: c193b46 0455029
Author: Michal Marek
Date:   Wed Sep 29 14:10:52 2010 +0000

    Merge branch 'kconfig' into for-next

diff --cc arch/mips/Kconfig
index 3ad59dd,3377167..9f61741
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@@ -10,12 -10,8 +10,10 @@@ config MIP
        select HAVE_DYNAMIC_FTRACE
        select HAVE_FTRACE_MCOUNT_RECORD
        select HAVE_FUNCTION_GRAPH_TRACER
 +      select HAVE_KPROBES
 +      select HAVE_KRETPROBES
        select RTC_LIB if !MACH_LOONGSON
  
- mainmenu "Linux/MIPS Kernel Configuration"
- 
  menu "Machine selection"
  
  config ZONE_DMA

一見 "make oldconfig" で実行されるプログラム "scripts/kconfig/conf" には関係ないように見えます。でも、よく見るとこれはマージコミットなので、この二つの親から原因が来てるはずです。

親1 c193b46
           >--- 8f9b217448dfb8acf2b320960247d3139c9744a6
親2 0455029

問題のコードあたりにおける親とマージコミットの差分を見てみます。

% git diff c193b46 8f9b217448dfb8acf2b320960247d3139c9744a6 scripts/kconfig/
<snip> # とにかくいっぱい
% git diff 0455029 8f9b217448dfb8acf2b320960247d3139c9744a6 scripts/kconfig
diff --git a/scripts/kconfig/.gitignore b/scripts/kconfig/.gitignore
index 6a36a76..624f650 100644
--- a/scripts/kconfig/.gitignore
+++ b/scripts/kconfig/.gitignore
@@ -17,6 +17,7 @@ gconf.glade.h
 #
 conf
 mconf
+nconf
 qconf
 gconf
 kxgettext
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index eba5906..5459a38 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -598,12 +598,12 @@ int main(int ac, char **av)
                break;
        case savedefconfig:
                break;
-       case oldconfig:
        case oldaskconfig:
                rootEntry = &rootmenu;
                conf(&rootmenu);
                input_mode = silentoldconfig;
                /* fall through */
+       case oldconfig:
        case listnewconfig:
        case oldnoconfig:
        case silentoldconfig:

親2 "0455029" とマージコミットの差分はそんなにない、のでおそらくは "0455029" 系のツリーの中に bug を(潜在的に)起こす commit がある可能性が高いです。ログを調べます。

% git log ..0455029

……正直わかりません ^^;

省略した diff に戻ります。上からあやしそうな diff を探していきます。 printf とかが変わっているのはどうでもいいですね。

あやしげな diff を見つけました。

--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -221,24 +221,23 @@ load:
        while (fgets(line, sizeof(line), in)) {
                conf_lineno++;
                sym = NULL;
-               switch (line[0]) {
-               case '#':
-                       if (memcmp(line + 2, "CONFIG_", 7))
+               if (line[0] == '#') {
+                       if (memcmp(line + 2, CONFIG_, strlen(CONFIG_)))
                                continue;
-                       p = strchr(line + 9, ' ');
+                       p = strchr(line + 2 + strlen(CONFIG_), ' ');
                        if (!p)
                                continue;
                        *p++ = 0;
                        if (strncmp(p, "is not set", 10))
                                continue;
                        if (def == S_DEF_USER) {
-                               sym = sym_find(line + 9);
+                               sym = sym_find(line + 2 + strlen(CONFIG_));
                                if (!sym) {
                                        sym_add_change_count(1);
                                        break;
                                }
                        } else {

switch を if に書きかえているようです。ところが、 switch で使っていた break はそのまま残っていますね。これでは break は while の外側に出ていしってしまいます!

ログに戻ってそれっぽい commit の捜索...

% git log ..0455029
<snip>
commit 8baefd30b5b0101aa07aa75da44a9eee881eed28
Author: Arnaud Lacombe
Date:   Tue Aug 24 00:14:47 2010 -0400

    kconfig: replace a `switch()' statement by a more flexible `if()' statement
    
    With the upcoming dynamical configuration prefix, we can no longer assume that
    the prefix will start by a 'C'. As such, we can no longer hardcode this value in
    the `case ...:', so replace the `switch() { ... }' statement by a more flexible
    'if () { ... }' statement.
    
    Signed-off-by: Arnaud Lacombe
    Reviewed-by: Sam Ravnborg
    Reviewed-by: Michal Marek

間違いなくこれですね。内容を確認。

% git show 8baefd30b5b0101aa07aa75da44a9eee881eed28
commit 8baefd30b5b0101aa07aa75da44a9eee881eed28
Author: Arnaud Lacombe <lacombar@gmail.com>
Date:   Tue Aug 24 00:14:47 2010 -0400

    kconfig: replace a `switch()' statement by a more flexible `if()' statement
    
    With the upcoming dynamical configuration prefix, we can no longer assume that
    the prefix will start by a 'C'. As such, we can no longer hardcode this value in
    the `case ...:', so replace the `switch() { ... }' statement by a more flexible
    'if () { ... }' statement.
    
    Signed-off-by: Arnaud Lacombe <lacombar@gmail.com>
    Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
    Reviewed-by: Michal Marek <mmarek@suse.cz>

diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index dc11d51..d9181de 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -221,8 +221,7 @@ load:
        while (fgets(line, sizeof(line), in)) {
                conf_lineno++;
                sym = NULL;
-               switch (line[0]) {
-               case '#':
+               if (line[0] == '#') {
                        if (memcmp(line + 2, "CONFIG_", 7))
                                continue;
                        p = strchr(line + 9, ' ');
@@ -254,12 +253,7 @@ load:
                        default:
                                ;
                        }
-                       break;
-               case 'C':
-                       if (memcmp(line, "CONFIG_", 7)) {
-                               conf_warning("unexpected data");
-                               continue;
-                       }
+               } else if (memcmp(line, "CONFIG_", 7) == 0) {
                        p = strchr(line + 7, '=');
                        if (!p)
                                continue;
@@ -286,12 +280,9 @@ load:
                        }
                        if (conf_set_sym_val(sym, def, def_flags, p))
                                continue;
-                       break;
-               case '\r':
-               case '\n':
-                       break;
-               default:
-                       conf_warning("unexpected data");
+               } else {
+                       if (line[0] != '\r' && line[0] != '\n')
+                               conf_warning("unexpected data");
                        continue;
                }
                if (sym && sym_is_choice_value(sym)) {

やっぱり、ここで break の変更を忘れています。 break のところを if(){}else if{} ... else{} を脱出するように書き直してあげたところうまく "make oldconfig" が動くようになりました。

linux-next へ投げてみたところ無事マージされました。

http://thread.gmane.org/gmane.linux.kernel/1043051

(ほんとのところはこんなにうまくはいかず、 "NEW" で grep したり printf をはさんだりしてこの場所を特定しています…)