3 分で読了 #linux-kernel #file-system
このページの目次

NFS (Network File System)

カバー範囲: NFSv3/v4 プロトコル → SUNRPC レイヤー → クライアント/サーバーアーキテクチャ → キャッシング (属性/デレゲーション) → pNFS → Kerberos セキュリティ → nfsd カーネルバージョン: 2.6 ~ 6.x

概要

NFS は Unix/Linux において最も伝統的なネットワークファイルシステムです。NFSv4 (2003年) は v3 に対する大幅な書き換えであり、ステート管理 (ロックとデレゲーション)、複合操作 (compound RPC)、擬似ファイルシステム (pseudo-fs)、およびより強力なセキュリティモデルを導入しました。

Linux の NFS 実装は、2つの独立したモジュールに分かれています。

  • nfsd (サーバー, fs/nfsd/): ローカルファイルシステムをネットワークにエクスポート
  • nfs (クライアント, fs/nfs/): リモートファイルシステムをマウント

SUNRPC レイヤー

NFS はトランスポート層として SUNRPC (ONC RPC) を使用します。

// net/sunrpc/
// RPC クライアント: リクエストを送信し、応答を待機
struct rpc_clnt {
    struct rpc_program *cl_program;    // NFS (100003) または他のプログラム
    struct rpc_version *cl_versions;   // NFSv3 (3) または NFSv4 (4)
    struct rpc_xprt    *cl_xprt;       // トランスポート: TCP/UDP/RDMA
};

// RPC トランスポート:
//   TCP (デフォルト): 信頼性あり、組み込みフロー制御
//   UDP:       軽量だがパケット損失のリスクあり (v3 のみ)
//   RDMA:      高性能 (InfiniBand/RoCE)

NFS クライアント

マウントと擬似ファイルシステム

NFSv3 マウント:
  mount -t nfs server:/export/path /mnt
    → MOUNT プロトコル (portmapper) を使用して初期 filehandle を取得
    → 以降の操作は NFS プロトコル (ポート 2049) を使用

NFSv4 マウント:
  mount -t nfs4 server:/ /mnt
    → 擬似ファイルシステム: サーバー側でルートエクスポートを行い、クライアントはトラバーサルによってサブエクスポートに到達
    → 補助的な MOUNT プロトコルは不要
    → /etc/exports における fsid=0 はルートエクスポートを示す

属性キャッシュ

// NFS クライアントはファイル属性 (stat, 権限など) をキャッシュ
// キャッシュの有効期限は acregmin/acregmax/acdirmin/acdirmax で制御

// close-to-open 一貫性:
//   これは NFS のデフォルトの一貫性モデルです:
//   1. open() → 属性の再検証を強制 (GETATTR)
//   2. 未開封のファイル: キャッシュ期間中は属性を再利用
//   3. close() → 変更をすべて書き戻し
//   4. 別のクライアントが同じファイルを open → 変更全体を確認可能

// NFSv4 デレゲーション (委譲):
//   サーバーはファイルの読み書き権限を「委譲」できる
//   クライアントが読み取り/書き取りを独占 → ローカルキャッシュの属性とデータ → RPC ゼロ!
//   別のクライアントがアクセスしようとする → サーバーはまずデレゲーションを取り消し (revoke) → 最初のクライアントがサーバーにフラッシュ
//   分散システムにおけるリース (lease) メカニズムに類似

Page Cache と書き戻し

NFS クライアントの page cache:
  読み込み: page cache ヒット → RPC を発行せず → ローカルでサービス提供
  書き込み: まず page cache に書き込み → 非同期 WRITE RPC → バックグラウンドで書き戻し

NFS COMMIT:
  fsync → COMMIT RPC → サーバーが安定したストレージへの書き込みを保証
  → サーバーは NFS write delegation を使用して高速化する場合がある

NFSv4.1+ pNFS (Parallel NFS):
  メタデータは MDS (Metadata Server) を経由
  データは DS (Data Server) に直接アクセス
  → MDS の帯域幅ボトルネックを回避

NFS サーバー (nfsd)

// fs/nfsd/
// カーネル NFS サーバー: nfsd カーネルスレッドプール

// ファイルシステムのエクスポート:
//   /etc/exports:
//     /export *(rw,sync,no_subtree_check)
//   exportfs -a → カーネルに通知

// nfsd スレッド:
//   各 CPU につき 1 つの nfsd カーネルスレッド
//   nfsd のループ: RPC リクエストの取得 → NFS 操作の実行 → 応答の返却
//   スレッド数: /proc/fs/nfsd/threads

// NFSv4 ステート:
//   サーバーはクライアントのステート (オープン中のファイル、ロック、デレゲーション) を維持
//   ステートストレージ: /var/lib/nfs/nfsdcltrack (または v4.2+ ではファイルシステムベース)
//   クライアントがクラッシュ → lease 時間後にステートが自動的に回収

セキュリティモデル

NFSv3: 
  AUTH_SYS (UNIX クレデンシャル: uid/gid/補助グループ) に基づく
  → クライアントが提供する uid/gid を信頼 (不安全!)
  → root_squash による緩和 (root を nobody にマッピング)
  → sec=sys (デフォルト), sec=krb5 (オプション)

NFSv4:
  Kerberos 5 (RPCSEC_GSS) をサポート
    sec=krb5:    認証のみ
    sec=krb5i:   認証 + 完全性 (改ざん防止、署名)
    sec=krb5p:   認証 + 完全性 + 機密性 (データ暗号化)

  必須要件: NFSv4.2 以降のサーバーは sec=krb5 をサポートする必要がある

TASK_KILLABLE と NFS

// NFS は TASK_KILLABLE が導入された主要な動機の一つ
// 古典的な問題: NFS がマウントされているがサーバーに到達できない
//   → プロセスが NFS マウントポイントにアクセス → TASK_UNINTERRUPTIBLE (D 状態) に移行
//   → SIGKILL が無効 (D 状態はシグナルに応答しない!)
//   → 再起動するしかない
//
// TASK_KILLABLE による解決 (5.x 以降):
//   一部の待機パスでは wait_event_killable() を使用
//   → 待機中に fatal_signal_pending() をチェック
//   → SIGKILL で中断可能
//   ただし、すべての NFS パスが KILLABLE に変更されたわけではない

デバッグ

# NFS クライアント統計
cat /proc/self/mountstats  # 詳細な RPC 統計
nfsstat -c                  # クライアント

# NFS サーバー統計
nfsstat -s                  # サーバー

# NFS マウントオプションの確認
mount | grep nfs
cat /proc/mounts | grep nfs

# NFS RPC のトレース
rpcdebug -m nfs -s all      # NFS クライアントのデバッグを有効化
rpcdebug -m nfsd -s all     # NFS サーバーのデバッグを有効化

参考資料と拡張

  • カーネルドキュメント⁠: Documentation/filesystems/nfs/
  • RFC: RFC 7530 (NFSv4), RFC 5661 (NFSv4.1/pNFS), RFC 7862 (NFSv4.2)
  • ソースコード⁠:
    • fs/nfs/ — クライアント実装
    • fs/nfsd/ — サーバー実装
    • net/sunrpc/ — RPC レイヤー
    • include/linux/sunrpc/ — RPC 型定義

キーワード: NFSv4, SUNRPC, delegation, close-to-open, pNFS, nfsd, TASK_KILLABLE, Kerberos, RPCSEC_GSS