こういうpatchが投稿されている
[10/12] writeback: only allow one inflight and pending full flush - Patchwork
full flushが一度に1つまでしか走らないようにしたいね、というものなのだけど、中身は本題には関係はない
patchの中に以下のようなコードがある
+ if (test_bit(WB_start_all, &wb->state))
+ return;
+
+ set_bit(WB_start_all, &wb->state);
"wb->state"のWB_start_allのbitが立っていたらreturnして、立ってなかったら立てる、というもの。このWB_start_allで"full flush"の排他をしていることになる。
厳密には、これは排他になっていない。1つ目のスレッドがtest_bit()してから、set_bit()するまでの間に、2つ目のスレッドがtest_bit()でbitを見にいくことがある。その場合、2つのスレッドが同時に"critical section"に入ってしまう。
ここを厳密な排他にするには、test_and_set_bit()を使う。
linux/atomic.h at master · torvalds/linux · GitHub
もちろんそんなことはkernel developerには、わかっている。patchのコメントは以下のように書いてある
It doesn't matter if we race a little bit on this, so use the faster separate test/set bit variants.
もともとfull syncが2つ以上一緒に走ると重いよね、ぐらいのものなのでここで厳密な排他は必要ないのだ。
こういうatomicityが必要でない場合には、test_bit()とset_bit()とを分けた方がいい時もある。
test_and_set_bit()を使うと必ずbitを立てる、すなわち値を更新してしまう。すると、cache lineがdirtyになる。頻繁に呼ばれ、多くの場合にbitが立っているような場所では、この「cache lineを汚すかどうか」が大きな違いになってくる。
で、頻繁に呼ばれるところで無駄にcache lineを汚さずにtest_and_set_bitしたいな〜となると以下のようなコードを書くことになる
if (test_bit(bit, addr) || test_and_set_bit(bit, addr))
...
スレッドはpatchの内容をすっかり離れて、こうした"idiom"のdocumentないし、それもあわせてなんか関数作っとく?という話になる。そこで関数の名前の話になり、"test_and_test_and_set_bit"がいいとか、"test_then_test_and_set_bit"がいいとか、patchの話どうしたみたいな感じになっていってる。
まあ以下のように解説があるぐらい知られている話なのだけど、関数名として見ると"test and test and set bit"ってなんかむずっとする名前だよな・・・と思った次第