msyncの闇に足を踏み込もう

# 今回のまとめ

- msync(MS_ASYNC)は、何もしないを実行する（意味不明）

# msyncの闇に足を踏み込もう

https://www.oreilly.co.jp/community/blog/2010/09/buffer-cache-and-aio-part2.html

>この場合、メモリ領域がディスクへ書き戻す必要があることを通知するのはmsync(2)です。MS_ASYNCを指定すると、通知するだけでシステムコールは終了します。MS_SYNCを指定すると、通知に加え、内部でfsync(2)相当の処理も行われ、ディスクにまで書き戻します。

このあたりのソースコードを読んでいく。

# Linux Kernelのコメント

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/mm/msync.c

>/*
 * MS_SYNC syncs the entire file - including mappings.
 *
 * MS_ASYNC does not start I/O (it used to, up to 2.5.67).
 * Nor does it marks the relevant pages dirty (it used to up to 2.6.17).
 * Now it doesn't do anything, since dirty pages are properly tracked.
 *
 * The application may now run fsync() to
 * write out the dirty pages and wait on the writeout and check the result.
 * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
 * async writeout immediately.
 * So by _not_ starting I/O in MS_ASYNC we provide complete flexibility to
 * applications.
 */

MS_SYNC は、mappingを含むすべてのファイルを同期を取る。
MS_ASYNCは、I/Oを開始しません（2.5.67以前までは利用していました）。
更に、dirty pagesと関連付けてマークすることもしません（2.6.17以前まではマークしていました）。
現時点では、ダーティページは適切に追跡されるので、今は何もしていません。

アプリケーションは、dirty pagesを書きだすため、書き込みが終わるあるいは結果をチェックするためにfsync()を実行できる。
あるいは、アプリケーションは即座に非同期に書き出すために、fadvise(FADV_DONTNEED)を実行できる。
したがって、MS_ASYNCにおいて、I/Oを開始しないことによって、アプリケーションに対して完全な柔軟性を提供できる。

# 実際のソースコードを見てみる

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/mm/msync.c#n61

重要な部分だけを抜き出すと、

```
	/*
	 * If the interval [start,end) covers some unmapped address ranges,
	 * just ignore them, but return -ENOMEM at the end.
	 */
	down_read(&mm->mmap_sem);
	vma = find_vma(mm, start);
	for (;;) {

＜略＞

		start = vma->vm_end;
		if ((flags & MS_SYNC) && file &&
				(vma->vm_flags & VM_SHARED)) {
			get_file(file);
			up_read(&mm->mmap_sem);
			error = vfs_fsync_range(file, fstart, fend, 1);
			fput(file);
			if (error || start >= end)
				goto out;
			down_read(&mm->mmap_sem);
			vma = find_vma(mm, start);
		} else {
			if (start >= end) {
				error = 0;
				goto out_unlock;
			}
			vma = vma->vm_next;
		}
	}
```

ということで、MS_SYNCが指定されていてマッピングできていれば、vfs_fsync_range()とfput(file)によって反映させる。
そうでない場合には、本当に何もしません、と。

そうしたら、MS_ASYNC指定したら、いつ反映されるのか？は、dirty pageの扱いで、pdflush()によるデフォルト5秒ごとの更新、と。

https://naoya-2.hatenadiary.org/entry/20070523/1179938637

# きれいにしたのはこちらのパッチ

どうやら、dirty statusのハンドリングが自動化できるようになったので、わざわざフラグ設定するのはムダになった、を含めてリファクタリングしたようでございますね。なるほどなるほど。

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/mm/msync.c?id=204ec841fbea3e5138168edbc3a76d46747cc987
 
> [PATCH] mm: msync() cleanup
> With the tracking of dirty pages properly done now, msync doesn't need to scan the PTEs anymore to determine the dirty status.
>  
> From: Hugh Dickins <hugh＠veritas.com>
> 
In looking to do that, I made some other tidyups: can remove several #includes, and sys_msync loop termination not quite right.
> 
> Most of those points are criticisms of the existing sys_msync, not of your patch.  
> In particular, the loop termination errors were introduced in 2.6.17: I did notice this shortly before it came out, but decided I was more likely to get it wrong myself, and make matters worse if I tried to rush a last-minute fix in.  And it's not terribly likely to go wrong, nor disastrous if it does go wrong (may miss reporting an unmapped area; may also fsync file of a following vma).

ダーティページの追跡が適切に行うようになったため、msyncではdirty statusを判別するために、PTEをスキャンする必要がなくなりました。

From: Hugh Dickins <hugh＠veritas.com>

これを実行するためには、私は不要なinludeを削除するなどいくつかの整理を行いました。そして、sys_nsyncのループ終了条件が正しくありませんでした。

これらの点のほとんどは、あなたのパッチではなく、既存のsys_msyncへの問題指摘です。特に、ループ終了エラーについては2.6.17で導入されていました。この問題が起きる少し前に気が付きましたが、自分で間違える可能性が高く、土壇場で修正をすると自体がさらに悪化する事になりました。それはうまくいきませんでした（マップされていない領域の報告を見逃す可能性があります、また次のvmaのfsyncファイルもある可能性があります）。