← Documents Documentation/filesystems/nfs/exporting.rst GitHub 원문 ↗

Linux 6.18.37 · Filesystems / NFS

Making Filesystems Exportable

Filehandle fragment, disconnected dentry, export_operations와 NFSD export flag의 전문 번역입니다.

Source pathDocumentation/filesystems/nfs/exporting.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

exporting.rst:1-240

Exportable filesystem은 rename과 reboot를 견디는 opaque filehandle fragment와 dentry 사이 mapping을 구현합니다. Filehandle에서 직접 발견한 disconnected dentry를 dcache invariant에 맞게 관리하고, lookup에서는 `d_splice_alias`로 기존 directory alias를 재연결해야 합니다.

`struct export_operations`는 encode/decode와 parent/name 복원 callback을 제공하고, flag는 WCC, subtree checking, unlink, remote writeback, attribute atomicity, close-time flush에 관한 filesystem 특성을 nfsd에 전달합니다.

Filesystem export 구현 경로
Stable filehandle fragment 설계Disconnected dentry와 alias 재연결 구현struct export_operations callback 등록Filesystem 의미에 맞는 EXPORT_OP_* flag 설정NFSD가 remote lookup과 operation 수행

Filehandle mapping부터 NFSD별 동작 조정까지의 계층입니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 :orphan:
2
3 Making Filesystems Exportable
4 =============================
5
6 Overview
7 --------
8
9 All filesystem operations require a dentry (or two) as a starting
10 point. Local applications have a reference-counted hold on suitable
11 dentries via open file descriptors or cwd/root. However remote
12 applications that access a filesystem via a remote filesystem protocol
13 such as NFS may not be able to hold such a reference, and so need a
14 different way to refer to a particular dentry. As the alternative
15 form of reference needs to be stable across renames, truncates, and
16 server-reboot (among other things, though these tend to be the most
17 problematic), there is no simple answer like 'filename'.
18
19 The mechanism discussed here allows each filesystem implementation to
20 specify how to generate an opaque (outside of the filesystem) byte
21 string for any dentry, and how to find an appropriate dentry for any
22 given opaque byte string.
23 This byte string will be called a "filehandle fragment" as it
24 corresponds to part of an NFS filehandle.
25
26 A filesystem which supports the mapping between filehandle fragments
27 and dentries will be termed "exportable".
28
29
30
31 Dcache Issues
32 -------------
33
34 The dcache normally contains a proper prefix of any given filesystem
35 tree. This means that if any filesystem object is in the dcache, then
36 all of the ancestors of that filesystem object are also in the dcache.
37 As normal access is by filename this prefix is created naturally and
38 maintained easily (by each object maintaining a reference count on
39 its parent).
40
41 However when objects are included into the dcache by interpreting a
42 filehandle fragment, there is no automatic creation of a path prefix
43 for the object. This leads to two related but distinct features of
44 the dcache that are not needed for normal filesystem access.
45
46 1. The dcache must sometimes contain objects that are not part of the
47 proper prefix. i.e that are not connected to the root.
48 2. The dcache must be prepared for a newly found (via ->lookup) directory
49 to already have a (non-connected) dentry, and must be able to move
50 that dentry into place (based on the parent and name in the
51 ->lookup). This is particularly needed for directories as
52 it is a dcache invariant that directories only have one dentry.
53
54 To implement these features, the dcache has:
55
56 a. A dentry flag DCACHE_DISCONNECTED which is set on
57 any dentry that might not be part of the proper prefix.
58 This is set when anonymous dentries are created, and cleared when a
59 dentry is noticed to be a child of a dentry which is in the proper
60 prefix. If the refcount on a dentry with this flag set
61 becomes zero, the dentry is immediately discarded, rather than being
62 kept in the dcache. If a dentry that is not already in the dcache
63 is repeatedly accessed by filehandle (as NFSD might do), an new dentry
64 will be a allocated for each access, and discarded at the end of
65 the access.
66
67 Note that such a dentry can acquire children, name, ancestors, etc.
68 without losing DCACHE_DISCONNECTED - that flag is only cleared when
69 subtree is successfully reconnected to root. Until then dentries
70 in such subtree are retained only as long as there are references;
71 refcount reaching zero means immediate eviction, same as for unhashed
72 dentries. That guarantees that we won't need to hunt them down upon
73 umount.
74
75 b. A primitive for creation of secondary roots - d_obtain_root(inode).
76 Those do _not_ bear DCACHE_DISCONNECTED. They are placed on the
77 per-superblock list (->s_roots), so they can be located at umount
78 time for eviction purposes.
79
80 c. Helper routines to allocate anonymous dentries, and to help attach
81 loose directory dentries at lookup time. They are:
82
83 d_obtain_alias(inode) will return a dentry for the given inode.
84 If the inode already has a dentry, one of those is returned.
85
86 If it doesn't, a new anonymous (IS_ROOT and
87 DCACHE_DISCONNECTED) dentry is allocated and attached.
88
89 In the case of a directory, care is taken that only one dentry
90 can ever be attached.
91
92 d_splice_alias(inode, dentry) will introduce a new dentry into the tree;
93 either the passed-in dentry or a preexisting alias for the given inode
94 (such as an anonymous one created by d_obtain_alias), if appropriate.
95 It returns NULL when the passed-in dentry is used, following the calling
96 convention of ->lookup.
97
98 Filesystem Issues
99 -----------------
100
101 For a filesystem to be exportable it must:
102
103 1. provide the filehandle fragment routines described below.
104 2. make sure that d_splice_alias is used rather than d_add
105 when ->lookup finds an inode for a given parent and name.
106
107 If inode is NULL, d_splice_alias(inode, dentry) is equivalent to::
108
109 d_add(dentry, inode), NULL
110
111 Similarly, d_splice_alias(ERR_PTR(err), dentry) = ERR_PTR(err)
112
113 Typically the ->lookup routine will simply end with a::
114
115 return d_splice_alias(inode, dentry);
116 }
117
118
119
120 A file system implementation declares that instances of the filesystem
121 are exportable by setting the s_export_op field in the struct
122 super_block. This field must point to a "struct export_operations"
123 struct which has the following members:
124
125 encode_fh (mandatory)
126 Takes a dentry and creates a filehandle fragment which may later be used
127 to find or create a dentry for the same object.
128
129 fh_to_dentry (mandatory)
130 Given a filehandle fragment, this should find the implied object and
131 create a dentry for it (possibly with d_obtain_alias).
132
133 fh_to_parent (optional but strongly recommended)
134 Given a filehandle fragment, this should find the parent of the
135 implied object and create a dentry for it (possibly with
136 d_obtain_alias). May fail if the filehandle fragment is too small.
137
138 get_parent (optional but strongly recommended)
139 When given a dentry for a directory, this should return a dentry for
140 the parent. Quite possibly the parent dentry will have been allocated
141 by d_alloc_anon. The default get_parent function just returns an error
142 so any filehandle lookup that requires finding a parent will fail.
143 ->lookup("..") is *not* used as a default as it can leave ".." entries
144 in the dcache which are too messy to work with.
145
146 get_name (optional)
147 When given a parent dentry and a child dentry, this should find a name
148 in the directory identified by the parent dentry, which leads to the
149 object identified by the child dentry. If no get_name function is
150 supplied, a default implementation is provided which uses vfs_readdir
151 to find potential names, and matches inode numbers to find the correct
152 match.
153
154 flags
155 Some filesystems may need to be handled differently than others. The
156 export_operations struct also includes a flags field that allows the
157 filesystem to communicate such information to nfsd. See the Export
158 Operations Flags section below for more explanation.
159
160 A filehandle fragment consists of an array of 1 or more 4byte words,
161 together with a one byte "type".
162 The decode_fh routine should not depend on the stated size that is
163 passed to it. This size may be larger than the original filehandle
164 generated by encode_fh, in which case it will have been padded with
165 nuls. Rather, the encode_fh routine should choose a "type" which
166 indicates the decode_fh how much of the filehandle is valid, and how
167 it should be interpreted.
168
169 Export Operations Flags
170 -----------------------
171 In addition to the operation vector pointers, struct export_operations also
172 contains a "flags" field that allows the filesystem to communicate to nfsd
173 that it may want to do things differently when dealing with it. The
174 following flags are defined:
175
176 EXPORT_OP_NOWCC - disable NFSv3 WCC attributes on this filesystem
177 RFC 1813 recommends that servers always send weak cache consistency
178 (WCC) data to the client after each operation. The server should
179 atomically collect attributes about the inode, do an operation on it,
180 and then collect the attributes afterward. This allows the client to
181 skip issuing GETATTRs in some situations but means that the server
182 is calling vfs_getattr for almost all RPCs. On some filesystems
183 (particularly those that are clustered or networked) this is expensive
184 and atomicity is difficult to guarantee. This flag indicates to nfsd
185 that it should skip providing WCC attributes to the client in NFSv3
186 replies when doing operations on this filesystem. Consider enabling
187 this on filesystems that have an expensive ->getattr inode operation,
188 or when atomicity between pre and post operation attribute collection
189 is impossible to guarantee.
190
191 EXPORT_OP_NOSUBTREECHK - disallow subtree checking on this fs
192 Many NFS operations deal with filehandles, which the server must then
193 vet to ensure that they live inside of an exported tree. When the
194 export consists of an entire filesystem, this is trivial. nfsd can just
195 ensure that the filehandle live on the filesystem. When only part of a
196 filesystem is exported however, then nfsd must walk the ancestors of the
197 inode to ensure that it's within an exported subtree. This is an
198 expensive operation and not all filesystems can support it properly.
199 This flag exempts the filesystem from subtree checking and causes
200 exportfs to get back an error if it tries to enable subtree checking
201 on it.
202
203 EXPORT_OP_CLOSE_BEFORE_UNLINK - always close cached files before unlinking
204 On some exportable filesystems (such as NFS) unlinking a file that
205 is still open can cause a fair bit of extra work. For instance,
206 the NFS client will do a "sillyrename" to ensure that the file
207 sticks around while it's still open. When reexporting, that open
208 file is held by nfsd so we usually end up doing a sillyrename, and
209 then immediately deleting the sillyrenamed file just afterward when
210 the link count actually goes to zero. Sometimes this delete can race
211 with other operations (for instance an rmdir of the parent directory).
212 This flag causes nfsd to close any open files for this inode _before_
213 calling into the vfs to do an unlink or a rename that would replace
214 an existing file.
215
216 EXPORT_OP_REMOTE_FS - Backing storage for this filesystem is remote
217 PF_LOCAL_THROTTLE exists for loopback NFSD, where a thread needs to
218 write to one bdi (the final bdi) in order to free up writes queued
219 to another bdi (the client bdi). Such threads get a private balance
220 of dirty pages so that dirty pages for the client bdi do not imact
221 the daemon writing to the final bdi. For filesystems whose durable
222 storage is not local (such as exported NFS filesystems), this
223 constraint has negative consequences. EXPORT_OP_REMOTE_FS enables
224 an export to disable writeback throttling.
225
226 EXPORT_OP_NOATOMIC_ATTR - Filesystem does not update attributes atomically
227 EXPORT_OP_NOATOMIC_ATTR indicates that the exported filesystem
228 cannot provide the semantics required by the "atomic" boolean in
229 NFSv4's change_info4. This boolean indicates to a client whether the
230 returned before and after change attributes were obtained atomically
231 with the respect to the requested metadata operation (UNLINK,
232 OPEN/CREATE, MKDIR, etc).
233
234 EXPORT_OP_FLUSH_ON_CLOSE - Filesystem flushes file data on close(2)
235 On most filesystems, inodes can remain under writeback after the
236 file is closed. NFSD relies on client activity or local flusher
237 threads to handle writeback. Certain filesystems, such as NFS, flush
238 all of an inode's dirty data on last close. Exports that behave this
239 way should set EXPORT_OP_FLUSH_ON_CLOSE so that NFSD knows to skip
240 waiting for writeback when closing such files.
241

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Exportable filesystem과 filehandle fragment

1-29

모든 filesystem operation은 시작점으로 dentry 하나 또는 둘이 필요합니다. Local application은 open file descriptor나 `cwd`/root를 통해 적절한 dentry의 reference count를 유지할 수 있지만, NFS 같은 remote filesystem protocol로 접근하는 remote application은 그런 reference를 계속 보유하지 못할 수 있습니다.

Remote reference는 rename, truncate, server reboot를 거쳐서도 안정적이어야 하므로 filename만으로는 충분하지 않습니다. 여기서 설명하는 mechanism은 filesystem 구현이 임의의 dentry를 opaque byte string으로 encode하고, 주어진 opaque byte string으로 대응하는 dentry를 다시 찾는 방법을 정의하게 합니다.

이 byte string은 NFS filehandle의 일부에 해당하므로 `filehandle fragment`라고 부릅니다. Filehandle fragment와 dentry 사이의 양방향 mapping을 지원하는 filesystem을 `exportable` filesystem이라고 합니다.

Remote dentry 참조
Filesystem object의 dentryFilesystem별 encode로 opaque filehandle fragment 생성Rename·truncate·server reboot를 거쳐 fragment 전달Filesystem별 decode로 적절한 dentry 복원

경로명 대신 안정적인 filehandle fragment를 사용하는 이유입니다.

:orphan:

Making Filesystems Exportable
=============================

Overview
--------

All filesystem operations require a dentry (or two) as a starting
point.  Local applications have a reference-counted hold on suitable
dentries via open file descriptors or cwd/root.  However remote
applications that access a filesystem via a remote filesystem protocol
such as NFS may not be able to hold such a reference, and so need a
different way to refer to a particular dentry.  As the alternative
form of reference needs to be stable across renames, truncates, and
server-reboot (among other things, though these tend to be the most
problematic), there is no simple answer like 'filename'.

The mechanism discussed here allows each filesystem implementation to
specify how to generate an opaque (outside of the filesystem) byte
string for any dentry, and how to find an appropriate dentry for any
given opaque byte string.
This byte string will be called a "filehandle fragment" as it
corresponds to part of an NFS filehandle.

A filesystem which supports the mapping between filehandle fragments
and dentries will be termed "exportable".

Disconnected dentry와 dcache 재연결

30-97

일반적으로 dcache는 filesystem tree의 proper prefix를 포함합니다. 어떤 object가 dcache에 있으면 그 object의 모든 ancestor도 dcache에 있으며, filename 기반의 정상 접근에서는 child가 parent reference를 유지하므로 이 prefix가 자연스럽게 만들어집니다.

그러나 filehandle fragment를 해석해 object를 dcache에 넣을 때는 path prefix가 자동으로 만들어지지 않습니다. 따라서 dcache는 root에 연결되지 않은 object를 일시적으로 포함할 수 있어야 하고, `->lookup`으로 새로 찾은 directory에 이미 disconnected dentry가 있다면 directory당 dentry 하나라는 invariant를 지키면서 그 dentry를 parent와 name 위치로 옮길 수 있어야 합니다.

익명 dentry에는 `DCACHE_DISCONNECTED` flag가 설정됩니다. Proper prefix에 속한 dentry의 child라는 사실이 확인되면 flag를 지웁니다. Flag가 남은 dentry의 refcount가 0이 되면 dcache에 보관하지 않고 즉시 폐기하므로, NFSD가 dcache 밖 object를 filehandle로 반복 접근하면 매번 새 dentry가 할당되고 접근 종료 시 제거될 수 있습니다.

Disconnected dentry가 child, name, ancestor를 얻더라도 subtree 전체가 root에 성공적으로 재연결되기 전에는 `DCACHE_DISCONNECTED`가 유지됩니다. 이 subtree의 dentry는 reference가 있는 동안만 살아 있고 refcount 0에서 unhashed dentry처럼 즉시 evict되므로 unmount 때 별도로 찾아 제거할 필요가 없습니다.

`d_obtain_root(inode)`는 secondary root를 만들며 이 root에는 `DCACHE_DISCONNECTED`를 설정하지 않습니다. 대신 superblock별 `->s_roots` 목록에 넣어 unmount 시 찾아 evict할 수 있게 합니다.

`d_obtain_alias(inode)`는 기존 dentry가 있으면 하나를 반환하고, 없으면 `IS_ROOT`와 `DCACHE_DISCONNECTED` 상태의 익명 dentry를 만들어 inode에 붙입니다. Directory에는 dentry 하나만 붙도록 보장합니다. `d_splice_alias(inode, dentry)`는 전달된 dentry 또는 기존 alias를 tree에 연결하고, 전달된 dentry를 사용하면 `->lookup` convention에 따라 `NULL`을 반환합니다.

Dcache helper와 수명 규칙
요소역할수명/반환 규칙
`DCACHE_DISCONNECTED`Root와 아직 연결되지 않은 dentry 표시refcount 0이면 즉시 evict
`d_obtain_root(inode)`Secondary root 생성`->s_roots`에서 unmount 시 회수
`d_obtain_alias(inode)`기존 alias 반환 또는 익명 dentry 생성Directory alias 하나 보장
`d_splice_alias(inode, dentry)`Loose alias를 lookup 위치에 연결전달 dentry 사용 시 `NULL`

Filehandle로 발견한 object를 tree에 안전하게 연결하는 요소입니다.


Dcache Issues
-------------

The dcache normally contains a proper prefix of any given filesystem
tree.  This means that if any filesystem object is in the dcache, then
all of the ancestors of that filesystem object are also in the dcache.
As normal access is by filename this prefix is created naturally and
maintained easily (by each object maintaining a reference count on
its parent).

However when objects are included into the dcache by interpreting a
filehandle fragment, there is no automatic creation of a path prefix
for the object.  This leads to two related but distinct features of
the dcache that are not needed for normal filesystem access.

1. The dcache must sometimes contain objects that are not part of the
   proper prefix. i.e that are not connected to the root.
2. The dcache must be prepared for a newly found (via ->lookup) directory
   to already have a (non-connected) dentry, and must be able to move
   that dentry into place (based on the parent and name in the
   ->lookup).   This is particularly needed for directories as
   it is a dcache invariant that directories only have one dentry.

To implement these features, the dcache has:

a. A dentry flag DCACHE_DISCONNECTED which is set on
   any dentry that might not be part of the proper prefix.
   This is set when anonymous dentries are created, and cleared when a
   dentry is noticed to be a child of a dentry which is in the proper
   prefix.  If the refcount on a dentry with this flag set
   becomes zero, the dentry is immediately discarded, rather than being
   kept in the dcache.  If a dentry that is not already in the dcache
   is repeatedly accessed by filehandle (as NFSD might do), an new dentry
   will be a allocated for each access, and discarded at the end of
   the access.

   Note that such a dentry can acquire children, name, ancestors, etc.
   without losing DCACHE_DISCONNECTED - that flag is only cleared when
   subtree is successfully reconnected to root.  Until then dentries
   in such subtree are retained only as long as there are references;
   refcount reaching zero means immediate eviction, same as for unhashed
   dentries.  That guarantees that we won't need to hunt them down upon
   umount.

b. A primitive for creation of secondary roots - d_obtain_root(inode).
   Those do _not_ bear DCACHE_DISCONNECTED.  They are placed on the
   per-superblock list (->s_roots), so they can be located at umount
   time for eviction purposes.

c. Helper routines to allocate anonymous dentries, and to help attach
   loose directory dentries at lookup time. They are:

    d_obtain_alias(inode) will return a dentry for the given inode.
      If the inode already has a dentry, one of those is returned.

      If it doesn't, a new anonymous (IS_ROOT and
      DCACHE_DISCONNECTED) dentry is allocated and attached.

      In the case of a directory, care is taken that only one dentry
      can ever be attached.

    d_splice_alias(inode, dentry) will introduce a new dentry into the tree;
      either the passed-in dentry or a preexisting alias for the given inode
      (such as an anonymous one created by d_obtain_alias), if appropriate.
      It returns NULL when the passed-in dentry is used, following the calling
      convention of ->lookup.

Exportable filesystem의 lookup 요구 사항

98-119

Filesystem이 exportable하려면 뒤에서 설명하는 filehandle fragment routine을 제공해야 합니다. 또한 `->lookup`이 주어진 parent와 name에 해당하는 inode를 찾았을 때 `d_add`가 아니라 `d_splice_alias`를 사용해야 합니다.

`inode`가 `NULL`이면 `d_splice_alias(inode, dentry)`는 `d_add(dentry, inode)`를 수행하고 `NULL`을 반환하는 것과 같습니다. `inode`가 `ERR_PTR(err)`이면 그대로 `ERR_PTR(err)`를 반환합니다.

따라서 일반적인 `->lookup` 구현은 찾은 inode와 lookup dentry를 `d_splice_alias`에 넘겨 그 반환값을 그대로 돌려주는 형태로 끝납니다.

return d_splice_alias(inode, dentry);
}
Exportable lookup
Parent와 name으로 inode 탐색inode 또는 ERR_PTR/NULL 결과 확보d_splice_alias(inode, dentry) 호출기존 anonymous alias 또는 전달 dentry를 tree에 연결Helper 반환값을 ->lookup 결과로 반환

`->lookup` 결과를 기존 alias와 안전하게 합치는 흐름입니다.

Filesystem Issues
-----------------

For a filesystem to be exportable it must:

   1. provide the filehandle fragment routines described below.
   2. make sure that d_splice_alias is used rather than d_add
      when ->lookup finds an inode for a given parent and name.

      If inode is NULL, d_splice_alias(inode, dentry) is equivalent to::

                d_add(dentry, inode), NULL

      Similarly, d_splice_alias(ERR_PTR(err), dentry) = ERR_PTR(err)

      Typically the ->lookup routine will simply end with a::

                return d_splice_alias(inode, dentry);
        }


struct export_operations와 fragment 형식

120-168

Filesystem instance는 `struct super_block`의 `s_export_op` field를 설정해 export 가능함을 선언합니다. 이 field는 `struct export_operations`를 가리키며, 구현은 filehandle encode/decode와 parent/name 복원을 위한 operation을 제공합니다.

필수 `encode_fh`는 dentry에서 filehandle fragment를 만들어 나중에 같은 object의 dentry를 찾거나 생성할 수 있게 합니다. 필수 `fh_to_dentry`는 fragment가 가리키는 object를 찾고, 필요하면 `d_obtain_alias`를 사용해 dentry를 만듭니다.

선택 사항이지만 강하게 권장되는 `fh_to_parent`는 fragment가 가리키는 object의 parent dentry를 복원합니다. Fragment가 너무 짧으면 실패할 수 있습니다. 역시 강하게 권장되는 `get_parent`는 directory dentry의 parent를 반환하며, 기본 구현은 error만 반환하므로 parent 탐색이 필요한 filehandle lookup은 실패합니다. `->lookup("..")`은 관리하기 어려운 `..` dcache entry를 남길 수 있어 기본 방식으로 사용하지 않습니다.

선택 `get_name`은 parent directory에서 child object로 이어지는 name을 찾습니다. 제공하지 않으면 기본 구현이 `vfs_readdir`로 후보 name을 찾고 inode number를 비교합니다. `flags` field는 filesystem별 특성을 nfsd에 전달합니다.

Filehandle fragment는 하나 이상의 4-byte word 배열과 1-byte `type`으로 구성됩니다. Decode routine은 전달된 size에 의존해서는 안 됩니다. `encode_fh`가 만든 원래 fragment보다 큰 buffer가 NUL padding과 함께 전달될 수 있기 때문입니다. 대신 `encode_fh`가 유효 길이와 해석 방식을 나타내는 `type`을 선택해야 합니다.

Export operation 계약
Callback/field필수 여부역할
`encode_fh`필수dentry를 filehandle fragment로 encode
`fh_to_dentry`필수fragment에서 object dentry 복원
`fh_to_parent`강력 권장fragment에서 parent 복원
`get_parent`강력 권장directory의 parent dentry 반환
`get_name`선택Parent 안에서 child name 탐색
`flags`선택 정보Filesystem 특성을 nfsd에 전달

`struct export_operations`의 callback별 필수 여부와 역할입니다.

A file system implementation declares that instances of the filesystem
are exportable by setting the s_export_op field in the struct
super_block.  This field must point to a "struct export_operations"
struct which has the following members:

  encode_fh (mandatory)
    Takes a dentry and creates a filehandle fragment which may later be used
    to find or create a dentry for the same object.

  fh_to_dentry (mandatory)
    Given a filehandle fragment, this should find the implied object and
    create a dentry for it (possibly with d_obtain_alias).

  fh_to_parent (optional but strongly recommended)
    Given a filehandle fragment, this should find the parent of the
    implied object and create a dentry for it (possibly with
    d_obtain_alias).  May fail if the filehandle fragment is too small.

  get_parent (optional but strongly recommended)
    When given a dentry for a directory, this should return  a dentry for
    the parent.  Quite possibly the parent dentry will have been allocated
    by d_alloc_anon.  The default get_parent function just returns an error
    so any filehandle lookup that requires finding a parent will fail.
    ->lookup("..") is *not* used as a default as it can leave ".." entries
    in the dcache which are too messy to work with.

  get_name (optional)
    When given a parent dentry and a child dentry, this should find a name
    in the directory identified by the parent dentry, which leads to the
    object identified by the child dentry.  If no get_name function is
    supplied, a default implementation is provided which uses vfs_readdir
    to find potential names, and matches inode numbers to find the correct
    match.

  flags
    Some filesystems may need to be handled differently than others. The
    export_operations struct also includes a flags field that allows the
    filesystem to communicate such information to nfsd. See the Export
    Operations Flags section below for more explanation.

A filehandle fragment consists of an array of 1 or more 4byte words,
together with a one byte "type".
The decode_fh routine should not depend on the stated size that is
passed to it.  This size may be larger than the original filehandle
generated by encode_fh, in which case it will have been padded with
nuls.  Rather, the encode_fh routine should choose a "type" which
indicates the decode_fh how much of the filehandle is valid, and how
it should be interpreted.

WCC와 subtree checking 제어 flag

169-202

`struct export_operations.flags`는 filesystem을 다르게 처리해야 한다는 정보를 nfsd에 전달합니다. `EXPORT_OP_NOWCC`는 해당 filesystem에서 NFSv3 weak cache consistency(WCC) attribute 제공을 끕니다.

RFC 1813은 operation 전 inode attribute를 원자적으로 수집하고 operation 뒤 다시 수집해 client에 WCC data를 보내도록 권장합니다. Client가 일부 `GETATTR`를 생략할 수 있지만 server는 거의 모든 RPC에서 `vfs_getattr`를 호출해야 합니다. Cluster 또는 network filesystem에서는 비용이 크고 전후 attribute 수집의 atomicity도 보장하기 어려울 수 있습니다.

따라서 `->getattr` inode operation이 비싸거나 전후 attribute 수집을 원자적으로 보장할 수 없다면 `EXPORT_OP_NOWCC`를 고려합니다. 이 flag가 있으면 nfsd는 해당 filesystem에서 수행한 NFSv3 operation의 reply에 WCC attribute를 넣지 않습니다.

`EXPORT_OP_NOSUBTREECHK`는 subtree checking을 허용하지 않습니다. 전체 filesystem export라면 nfsd가 filehandle이 같은 filesystem에 있는지만 확인하면 되지만, 일부 subtree만 export하면 inode ancestor를 따라 올라가 export 범위 안인지 검사해야 합니다. 이 과정은 비싸고 일부 filesystem은 올바르게 지원할 수 없습니다.

이 flag는 filesystem을 subtree checking에서 제외하며, `exportfs`가 해당 filesystem에 subtree checking을 활성화하려 하면 error를 받게 합니다.

Export validation flag
Flag끄는 기능적용 이유
`EXPORT_OP_NOWCC`NFSv3 reply의 WCC attribute`getattr` 비용 또는 전후 atomicity 보장 불가
`EXPORT_OP_NOSUBTREECHK`Ancestor walk 기반 subtree checking검사 비용 또는 filesystem 지원 불가

WCC attribute와 subtree 검사의 비용·보장 조건을 정리합니다.

Export Operations Flags
-----------------------
In addition to the operation vector pointers, struct export_operations also
contains a "flags" field that allows the filesystem to communicate to nfsd
that it may want to do things differently when dealing with it. The
following flags are defined:

  EXPORT_OP_NOWCC - disable NFSv3 WCC attributes on this filesystem
    RFC 1813 recommends that servers always send weak cache consistency
    (WCC) data to the client after each operation. The server should
    atomically collect attributes about the inode, do an operation on it,
    and then collect the attributes afterward. This allows the client to
    skip issuing GETATTRs in some situations but means that the server
    is calling vfs_getattr for almost all RPCs. On some filesystems
    (particularly those that are clustered or networked) this is expensive
    and atomicity is difficult to guarantee. This flag indicates to nfsd
    that it should skip providing WCC attributes to the client in NFSv3
    replies when doing operations on this filesystem. Consider enabling
    this on filesystems that have an expensive ->getattr inode operation,
    or when atomicity between pre and post operation attribute collection
    is impossible to guarantee.

  EXPORT_OP_NOSUBTREECHK - disallow subtree checking on this fs
    Many NFS operations deal with filehandles, which the server must then
    vet to ensure that they live inside of an exported tree. When the
    export consists of an entire filesystem, this is trivial. nfsd can just
    ensure that the filehandle live on the filesystem. When only part of a
    filesystem is exported however, then nfsd must walk the ancestors of the
    inode to ensure that it's within an exported subtree. This is an
    expensive operation and not all filesystems can support it properly.
    This flag exempts the filesystem from subtree checking and causes
    exportfs to get back an error if it tries to enable subtree checking
    on it.

Unlink, remote storage, attribute, close flag

203-240

`EXPORT_OP_CLOSE_BEFORE_UNLINK`는 unlink 전에 cached file을 항상 닫게 합니다. NFS 같은 filesystem에서 open file을 unlink하면 client가 file을 유지하려고 `sillyrename`을 수행할 수 있습니다. Reexport 환경에서는 nfsd가 file을 열고 있어 sillyrename 직후 link count가 0이 되면서 다시 삭제하는 추가 작업이 생기고, 이 삭제가 parent directory의 `rmdir` 같은 operation과 race할 수 있습니다.

이 flag를 설정하면 nfsd는 VFS에 unlink를 요청하거나 기존 file을 대체하는 rename을 요청하기 전에 해당 inode의 open file을 모두 닫습니다.

`EXPORT_OP_REMOTE_FS`는 filesystem의 backing storage가 remote임을 나타냅니다. `PF_LOCAL_THROTTLE`은 loopback NFSD thread가 client bdi에 쌓인 write를 해제하기 위해 final bdi에 write해야 할 때 private dirty-page balance를 제공합니다. Durable storage가 local이 아닌 exported NFS filesystem에서는 이 제약이 오히려 해로울 수 있으므로 이 flag로 writeback throttling을 비활성화할 수 있습니다.

`EXPORT_OP_NOATOMIC_ATTR`은 filesystem이 NFSv4 `change_info4`의 `atomic` boolean이 요구하는 의미를 제공하지 못함을 나타냅니다. 이 boolean은 `UNLINK`, `OPEN/CREATE`, `MKDIR` 같은 metadata operation 전후의 change attribute를 해당 operation과 원자적으로 얻었는지 client에 알립니다.

`EXPORT_OP_FLUSH_ON_CLOSE`는 filesystem이 `close(2)` 때 file data를 flush함을 나타냅니다. 일반 filesystem은 close 뒤에도 inode writeback이 남을 수 있어 NFSD가 client activity나 local flusher thread에 의존합니다. NFS처럼 마지막 close에서 inode의 dirty data를 모두 flush하는 export는 이 flag를 설정하여 nfsd가 close 시 별도의 writeback 대기를 생략하게 해야 합니다.

Export I/O 동작 flag
FlagNFSD 동작
`EXPORT_OP_CLOSE_BEFORE_UNLINK`Unlink 또는 replacement rename 전에 cached file close
`EXPORT_OP_REMOTE_FS`Remote backing storage export에서 writeback throttling 해제
`EXPORT_OP_NOATOMIC_ATTR``change_info4.atomic` 의미를 제공하지 못함을 표시
`EXPORT_OP_FLUSH_ON_CLOSE`Close 자체가 flush하므로 nfsd의 writeback 대기 생략

Reexport와 writeback 의미에 영향을 주는 flag입니다.

  EXPORT_OP_CLOSE_BEFORE_UNLINK - always close cached files before unlinking
    On some exportable filesystems (such as NFS) unlinking a file that
    is still open can cause a fair bit of extra work. For instance,
    the NFS client will do a "sillyrename" to ensure that the file
    sticks around while it's still open. When reexporting, that open
    file is held by nfsd so we usually end up doing a sillyrename, and
    then immediately deleting the sillyrenamed file just afterward when
    the link count actually goes to zero. Sometimes this delete can race
    with other operations (for instance an rmdir of the parent directory).
    This flag causes nfsd to close any open files for this inode _before_
    calling into the vfs to do an unlink or a rename that would replace
    an existing file.

  EXPORT_OP_REMOTE_FS - Backing storage for this filesystem is remote
    PF_LOCAL_THROTTLE exists for loopback NFSD, where a thread needs to
    write to one bdi (the final bdi) in order to free up writes queued
    to another bdi (the client bdi). Such threads get a private balance
    of dirty pages so that dirty pages for the client bdi do not imact
    the daemon writing to the final bdi. For filesystems whose durable
    storage is not local (such as exported NFS filesystems), this
    constraint has negative consequences. EXPORT_OP_REMOTE_FS enables
    an export to disable writeback throttling.

  EXPORT_OP_NOATOMIC_ATTR - Filesystem does not update attributes atomically
    EXPORT_OP_NOATOMIC_ATTR indicates that the exported filesystem
    cannot provide the semantics required by the "atomic" boolean in
    NFSv4's change_info4. This boolean indicates to a client whether the
    returned before and after change attributes were obtained atomically
    with the respect to the requested metadata operation (UNLINK,
    OPEN/CREATE, MKDIR, etc).

  EXPORT_OP_FLUSH_ON_CLOSE - Filesystem flushes file data on close(2)
    On most filesystems, inodes can remain under writeback after the
    file is closed. NFSD relies on client activity or local flusher
    threads to handle writeback. Certain filesystems, such as NFS, flush
    all of an inode's dirty data on last close. Exports that behave this
    way should set EXPORT_OP_FLUSH_ON_CLOSE so that NFSD knows to skip
    waiting for writeback when closing such files.