| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- :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".
- 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.
- 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);
- }
- 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.
- 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.
- 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.
- EXPORT_OP_ASYNC_LOCK - Indicates a capable filesystem to do async lock
- requests from lockd. Only set EXPORT_OP_ASYNC_LOCK if the filesystem has
- it's own ->lock() functionality as core posix_lock_file() implementation
- has no async lock request handling yet. For more information about how to
- indicate an async lock request from a ->lock() file_operations struct, see
- fs/locks.c and comment for the function vfs_lock_file().
|