トップ «前の日記(2012-03-14) 最新 次の日記(2012-03-16)» 編集



2012-03-15 :-(

_ 午前

0520 起床

0830 出勤

0900 検討

_ 午後

1300 検討

1500 自社業務

1700 退勤


1900 ぐったり

2100 災害用伝言ダイヤル(171) 練習

2130 飯。ブリの塩焼き

2230 日本酒gkgk


_ 買い物





_ [NetBSD][翻訳][posix_spawn]NetBSD Blog - posix_spawn syscall added posix_spawn システムコールの追加

February 26, 2012 posted by Martin Husemann

2012/2/26 Martin Husemann による投稿

Charles Zhang implemented the posix_spawn syscall during Google Summer of Code 2011. After a lot of polishing and rework based on feedback during public discussion of the code, this has now been committed to NetBSD-current.

Charles Zhang は Google Summer of Code 2011 の間に posix_spawn システムコールを実装した。公開討論のフィードバックを基にたくさんのコードを洗練し、再作業したものだ。現在これは NetBSD-current にコミットされている。

This caused some fallout and ended in a tight race with the imminent branch date for NetBSD 6. Now that the dust has settled, it is time for a look back at the mistakes made and lessons learned.

もうすぐ来る NetBSD 6 ブランチにおいて、競合の終了時{ ended in a tight race }にたまに異常終了する{ fallout }。現在は安定したので、ここでは過ちと教訓を振り返る。

What is posix_spawn? ( posix_spawn とは? )

Traditionally BSD systems used the vfork(2) hack to improve speed of process creation. However, this does (in general) not play well with multi-threaded applications. The posix_spawn call is a thread-safe way to create new processes and manipulate a tiny bit of state (like dup/close/open file descriptors) upfront.

従来の BSD システムではプロセス生成の時間短縮のために vfork(2) ハックが用いられている。しかし、(一般的に)これはマルチスレッドアプリケーションではよろしくない。posix_spawn を呼ぶとプロセス生成をスレッドセーフとし、いくつかの状態( dup/close/open のようなファイル記述子 )を操作する。

Work continued after GSoC ( GSoC 後の継続作業 )

The results Charles had at the end of his GSoC term were a working in-kernel implementation of posix_spawn and a few free-form test cases, one of which failed. The kernel code duplicated a lot of other code, which clearly was not acceptable for commit to the NetBSD source tree. The reason Charles solved it this way was the short time frame available - and that the best solution we could think of during the summer was very intrusive.

GSoC 終了時の Charles の結果は、カーネル内に posix_spawn を実装し、2, 3 の自由な形式のテストをおこない、1 つが失敗した。カーネルコードは他のたくさんのコードと重複しているので、これを綺麗にしないと NetBSD ソースツリーへのコミットは許されない。Charles は短時間のフレームを有効にすることで解決した。GSoC 期間中に考えられたこの最善の解決策はとても intrusive だ {intrusive ????}。

In preparation for a potential merge into the NetBSD code base, I reworked the code to avoid copying helper functions (like file descriptor manipulations for other processes), cleaned up and debugged a bit using a LOCKDEBUG kernel, which pointed out a few more issues. After solving those as well as intensively testing all error paths, I posted a patch for review.

NetBSD コードベースにちゃんと { potential???? }マージするための準備としては、コードからコピーした補助関数(他プロセスのファイルディスクリプタ操作など))を削除する作業をし、綺麗にして、課題以外のいくつかの点で LOCKDEBUG カーネルで少しデバッグする。これらを解決するだけでなく、集中的にすべてのテストをパスしたあと、レビューのためにパッチを投稿した。

At this point the integration was already prepared completely - a new syscall, new libc functions, new manual pages need a lot of set lists updates and test building a "release" at least once (preferably on an architecture providing 32bit compat libraries), furthermore the posix_spawn code needed (simple) machine-dependent code to be added to all architectures, which at least requires test-building a representative set of kernels.

統合の準備はすでに完了している。セットリストをアップデートし、最低でも release を構築する( できるならば 32bit アーキテクチャ互換ライブラリを提供したい )ためには、新しい syscall、新しい libc 関数、新しいマニュアルページが必要だ。

Another complete rework (別の完全な再作業)

In response to the posted, very intrusive, patch, YAMAMOTO Takashi suggested a pretty elegant way to solve the problem without a lot of the intrusive changes. The idea was simple, and it actually worked after a few adjustments. This led to another public patch for review.

この投稿へのレスポンスはとても intrusive だ。山本貴志さんによるパッチは、それほどたくさんの intrusive な変更をせずに問題を解決することができるとてもエレガントな方法を提案してくれた。アイデアはシンプルで、ちょっとした調整をするだけで動作するようになる。この led {?????} はレビューのため別の公開パッチとする。

This version already included an atf version of the test programs, which all passed (both on amd64 and sparc64). I felt pretty confident with this state and expected a smooth integration.

このバージョンでは既に ATF のテストプログラムも含んでいて、すべてパスした( amd64 と sparc64 の両方で )。この状態でとても自信を持てたし、滞りなく統合できそうだ。

Unexpected fallout (期待していない異常終了)

More for completeness I did a full test run (not only the posix_spawn related tests) - and found some unexpected test failures, all in rump based tests. I retried and got different failures. Suspicious - I did not touch rump, besides regenerating the syscall definitions. I rebooted a standard kernel (without posix_spawn), did a full test run and only got failures in the posix_spawn tests (of course). So something in the change must have broken something else.

まだすべてのテストを走らせていないし( posix_spawn 関連のテストだけでなく )、rump ベースのテストすべてのうちいくつか期待していないテスト失敗がある。再度試したら異なる失敗になった。rump には触れていないので、syscall 再定義により再生成されたところ以外があやしい。通常のカーネル( posix_spawn なし )で再起動し、全テストを実施したら当然ながら posix_spawn のテストだけが失敗した。つまり、変更したもの以外の他の何かが壊れているのだ。

Analysis was a painful process, so only a short summary of the results: the modified kernel exec path used a pointer to a kernel stack variable, which was later copied to a saved data structure - but the pointer was not adjusted accordingly. Later the pointer was referenced, and only a single bit checked. Depending on what was in memory at the stale old stack location at that time, a branch was taken or not. This caused the ELF auxiliary data vector to sometimes contain a different effective UID, and ld.elf_so switching into secure mode - in which case it ignores environment variables like LD_PRELOAD. This causes big failure in many test programs using rump (at least).

解析はしんどい作業だ。結果の短い概要しかない。exec path を適用したカーネルがカーネルスタックポインタの変数を使った場合、後でデータ構造を保存するためにコピーしているんだが、このポインタはそれに応じて調整されていない。後にこのポインタが参照され、1 bit のみチェックされる。このときの失効した古いスタック領域のメモリの内容に応じて、ブランチは取得されたりされなかったりする。これは、ELF auxiliary データベクターが時々異なる実効 UID { effective UID } を含むことが原因だった。その場合には LD_PRELOAD のような環境変数を無視する。これは rump(少なくとも)を使用している多くのテストプログラムで大きな障害が発生する。

While I was debugging this, discussions continued. We were not sure if we should add complex code like this to the kernel, where a pure userland implementation clearly is possible (FreeBSD uses this, for example). I did a few benchmark runs, but was unable to show any clear performance benefit for either implementation - the differences were in the sub-promille ranges, with noise in the 2-3 percent range, clearly no usable result from a statistical point of view. Another topic under discussion was the near planned branch for NetBSD 6. According to our rules, we do not want to add a syscall post-branch to a release branch.

これをデバッグしている間も議論は継続している。このような複合コードをカーネルに追加しようとすることは自信が無いものだ。純粋にユーザーランドに閉じた実装 {implementation clearly} は可能だ( 例えば FreeBSD はそうしている )。いくつかベンチマークを走らせたが、実装の間でのそれほど明確なパフォーマンスの向上{ benefit }は見られなかった。違いは sub-promille ranges が 2, 3% の間でノイズがあった。はっきりいって、総合的に見て残念な結果{ no usable result }である。この議論では他に直近の NetBSD 6 ブランチ計画があった。我々の規則によるとリリースブランチの後のブランチに syscall を追加するのは望ましくない。

Go ahead, finally! (ついに!)

The discussions ended with the core team voting for a kernel version, and the release engineering team voting for a pre-netbsd-6-branch integration. So I updated my posix_spawn source tree, did another test build, ran tests (again on amd64 and sparc64), updated again - and committed in a few steps.

カーネルバージョンについてのコアチームによる投票と、リリースエンジニアリングチームの pre-netbsd-6-branch との統合の投票で議論は終了する。posix_spawn ソースツリーをアップデートしたので、他のテストビルドをし、テストをし( amd64 と sparc64 で )、再度アップデートし、これらの手順についてコミットする。

Big fallout (ヤバイ異常終了)

Checking mails early next morning (a Sunday, before walking the dog) I found a PR already: running the m4 configure script crashed i386 and amd64 kernels. Tsutsui kindly had provided a backtrace in the report, and it looked suspiciously familiar to me. While walking the dog I thought about it and when I got home I checked: indeed I had seen and fixed this before, when testing error paths in the first instance of the change. However, when dropping all the intrusive modifications I had in my tree and redoing the version without them, I must have accidentally dropped the fix for this (it was in sys/uvm instead of sys/kern). No big deal, I had fixed it once already, so I could fix it again. Committed, asked for verification - and did get a NAK. However, with a different back trace this time. Tried on my amd64 notebook - worked for me. Duh?

翌朝メールをチェックして( ニチアサの犬の散歩に行く前に )、既に PR されているのを見つけた。i386 と amd64 カーネルで m4 設定スクリプトを走らせるとクラッシュする。筒井さんがレポートにバックトレースを提供してくれたんだが、私にはちょっと信じられなかった。犬の散歩をしている間 これについて考え、帰宅してから、修正する前にこの変更の最初の実例でテストがパスしたことをたしかに確認した。しかし自分のツリーですべての変更を破棄し、これら抜きのバージョンで再度実施したら、全て落た。この突然死を修正せねばならない( これは sys/kern を含む sys/uvm にある )。どうということはない。すでに一度修正したので再度修正できた。コミットし、適当かどうかを訊いてみたが、とくに拒否されなかった{ NAK }のだが、異論があればバックトレースする。自分の amd64 ノートブックで試したらウマくいった。あれ?

Looking at the code and fixing the second fallout now was straight forward, and also provided the hint why I did not see it before: I was not running a GENERIC kernel on my notebook, and had (some time way back in the past) removed options DIAGNOSTIC from this configuration. Stupid me!

コードを見ると第二の異常終了が修正されていることはすぐ分かった { straight forward ????? }。私が以前やったことのヒントがあった。設定から DIAGNOSTIC (ときどき過去に遡る方法として使われる) を削除した GENERIC カーネルを自分のノートブックで動作させていなかったのである。あたしって、ほんとバカ

I received more feedback (YAMAMOTO-san pointed out some race conditions) and had a discussion about the place where the test programs should live in the source. To not risk delaying the netbsd-6 branch, I applied a minimal fix for the races, moved the test programs - and added a few more test cases covering the initial m4 configure problems (the rework earlier had made it pretty simple now to test all error paths from atf test cases).

さらにフィードバックをもらった( 山本さんがいくつか競合状態を示してくれた )。ソースのどこにテストプログラムを設置するか議論した。netbsd-6 ブランチに間に合わないことは問題ではない。競合へのちょっとした修正を適用し、テストプログラムを移動し、初期設定 m4 問題をカバーするテストを追加した( この再作業は atf テストケースでは早い段階から全テストをパスしていた )

This caused the automatic test setup to crash on every run ("Tests did not complete"). At this point I am still not sure why I did not catch this before commit - but there is no point in arguing, human failure - my fault (most likely explanation: after the last changes to the test cases, I did not test again on amd64 but only sparc64 - the test cases triggered a KASSERT in the x86 pmap, but not in the sparc64 one).

これにより、自動テストセットアップは毎回クラッシュするようになった ("Tests did not complete")。この時点で私がだコミットする前にこれをキャッチしなかった理由は不明だ。しかし私の失敗が人為的ミスであったということを指し示すものはない。( 説明: テストケースを最後に変更した後に、sparc64 でのみテストし、amd64 ではテストしてない。このテストケースは x86 pmap で KASSERT のトリガーになるのだが、sparc64 にはないのである )。

I fixed this, and also another PR, interestingly about m4 configure again. Simple argument validation bug, not covered by the test cases yet - so I added another test.

これと他の PR も修正し、件の m4 を再度設定する。単純な引数の検証のバグはまだテストケースでカバーされていないので他のテストを追加した。

Are we there yet? (ほかには?)

Luckily fallout seems to have stopped now, but we are not completely there yet. The new process created by posix_spawn keeps the parent lwp blocked until it is done with all file descriptor modifications and setup, and the new process is ready to go to userland first time. This provides a proper error return value from the parent (the posix_spawn syscall itself), but it stops the new child from (for example) already running on another CPU early. This will be simple to change, but after all the fallout we have seen, I will only touch it after very extensive testing again.

運よく異常終了が停止したら、それはまだ完成していない箇所だ。posix_spawn によって生成された新しいプロセスは、全てのファイルディスクリプタを更新してセットアップする間 親の lwp をブロックする。新しいプロセスは、最初にユーザーランドとして準備される。この提供は、親から proper error を返す( posix_spawn システムコール自身による )。が、新しい子が、たとえばすでに別 CPU で動作していたりしたら、停止させる。この変更は簡単だろうけど、それでもまだ異常終了に遭遇する。再び非常に広範なテストの後に触れたいと思う。

Lessons learned (教訓)

When bringing in a new syscall with several supporting libc functions, fallout is always to be expected. It can be minimized by including test programs early - but in the end, real life will teach you what tests you have missed when writing the test programs. It is also important do full test suite runs early, and test on different architectures. Even better if you test on kernels with (at least) DIAGNOSTIC enabled. But in the end, mistakes will happen nevertheless.

いくつか新しいシステムコールに対応した libc 関数を使用すると異常終了がいつでも期待できるようになった。これは早期のテストプログラムによって最小限に抑えることができる。しかし最後にはテストプログラムを書くきに見逃していたテストを教えてくれる。それは、早期の完全テストを実施することが重要だということであり、異なるアーキテクチャでテストしろということだ。少なくとも DIAGNOSTIC を有効にしてカーネルをテストするとより良い。それでもやはりミスは起きるだろう。

本日のツッコミ(全2件) [ツッコミを入れる]
_ tsutsui (2012-03-17 22:25)

自分の関係するところだけコメント。<br>当時の雰囲気はこんな感じ?<br><br>---<br>However, when dropping all the intrusive modifications I had in my tree and redoing the version without them, I must have accidentally dropped the fix for this (it was in sys/uvm instead of sys/kern).<br>が、自分のツリー内のアレな修正を捨てて再度作り直したときにこの問題の修正(sys/kernではなくsys/uvmの中にある)をうっかり入れ忘れてたんだよー。<br><br>No big deal, I had fixed it once already, so I could fix it again.<br>どってことない、以前一回直しているんだからもう一度直すだけだ。<br><br>Committed, asked for verification - and did get a NAK.<br>コミットして確認を依頼した。そして「ダメ」の返事。(´・ω・`)<br><br>However, with a different back trace this time. <br>が、今回は別のバックトレースが添付されてた。<br><br>Looking at the code and fixing the second fallout now was straight forward, and also provided the hint why I did not see it before:<br><br>コードの確認と第二の問題の修正は単純な話で、そこになぜ自分ではその問題に遭遇しなかったのかのヒントがあった。<br><br>I was not running a GENERIC kernel on my notebook,<br>自分のノートでは GENERICカーネルを使ってなくて、<br> <br>and had (some time way back in the past) removed options DIAGNOSTIC from this configuration. <br>(いつの頃からか)そのカーネルコンフィグから options DIAGNOSTIC を外してしまってたの。

_ みわ (2012-03-17 23:24)

オウフ.... did not get NAK と読み違えてしまった。<br><br>読んでてなんとなく混乱した状態になってた雰囲気は把握できたんですがー (>'A`)><br><br><br>「ニチアサ」とか使ってみたかったんです (´・ω・`)