GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing docs.kernel.org — Network Filesystem Services Library — The Linux Kernel documentation Network Filesystem Services Library¶Overview¶The network filesystem services library, netfslib, is a set of functions designed to aid a network filesystem in implementing VM/VFS API operations. It takes over the normal buffered read, readahead, write and writeback and also handles unbuffered and direct I/O. The library provides support for (re-)negotiation of I/O sizes and retrying failed I/O as well as local caching and will, in the future, provide content encryption. It insulates the filesystem from VM interface changes as much as possible and handles VM features such as large multipage folios. The filesystem basically just has to provide a way to perform read and write RPC calls. The way I/O is organised inside netfslib consists of a number of objects:
Requests and Streams¶When actually performing I/O (as opposed to just copying into the pagecache), netfslib will create one or more requests to track the progress of the I/O and to hold resources. A read operation will have a single stream and the subrequests within that stream may be of mixed origins, for instance mixing RPC subrequests and cache subrequests. On the other hand, a write operation may have multiple streams, where each stream targets a different destination. For instance, there may be one stream writing to the local cache and one to the server. Currently, only two streams are allowed, but this could be increased if parallel writes to multiple servers is desired. The subrequests within a write stream do not need to match alignment or size with the subrequests in another write stream and netfslib performs the tiling of subrequests in each stream over the source buffer independently. Further, each stream may contain holes that don’t correspond to holes in the other stream. In addition, the subrequests do not need to correspond to the boundaries of the folios or vectors in the source/destination buffer. The library handles the collection of results and the wrangling of folio flags and references. Subrequests¶Subrequests are at the heart of the interaction between netfslib and the filesystem using it. Each subrequest is expected to correspond to a single read or write RPC or cache operation. The library will stitch together the results from a set of subrequests to provide a higher level operation. Netfslib has two interactions with the filesystem or the cache when setting up a subrequest. First, there’s an optional preparatory step that allows the filesystem to negotiate the limits on the subrequest, both in terms of maximum number of bytes and maximum number of vectors (e.g. for RDMA). This may involve negotiating with the server (e.g. cifs needing to acquire credits). And, secondly, there’s the issuing step in which the subrequest is handed off to the filesystem to perform. Note that these two steps are done slightly differently between read and write:
Once a subrequest is completed, the filesystem or cache informs the library of the completion and then collection is invoked. Depending on whether the request is synchronous or asynchronous, the collection of results will be done in either the application thread or in a work queue. Result Collection and Retry¶As subrequests complete, the results are collected and collated by the library and folio unlocking is performed progressively (if appropriate). Once the request is complete, async completion will be invoked (again, if appropriate). It is possible for the filesystem to provide interim progress reports to the library to cause folio unlocking to happen earlier if possible. If any subrequests fail, netfslib can retry them. It will wait until all subrequests are completed, offer the filesystem the opportunity to fiddle with the resources/state held by the request and poke at the subrequests before re-preparing and re-issuing the subrequests. This allows the tiling of contiguous sets of failed subrequest within a stream to be changed, adding more subrequests or ditching excess as necessary (for instance, if the network sizes change or the server decides it wants smaller chunks). Further, if one or more contiguous cache-read subrequests fail, the library will pass them to the filesystem to perform instead, renegotiating and retiling them as necessary to fit with the filesystem’s parameters rather than those of the cache. Local Caching¶One of the services netfslib provides, via Note that local caching used to use the PG_private_2 (aliased as PG_fscache) to keep track of a page that was being written to the cache, but this is now deprecated as PG_private_2 will be removed. Instead, folios that are read from the server for which there was no data in
the cache will be marked as dirty and will have When writeback occurs, folios that are so marked will only be written to the cache and not to the server. Writeback handles mixed cache-only writes and server-and-cache writes by using two streams, sending one to the cache and one to the server. The server stream will have gaps in it corresponding to those folios. Content Encryption (fscrypt)¶Though it does not do so yet, at some point netfslib will acquire the ability to do client-side content encryption on behalf of the network filesystem (Ceph, for example). fscrypt can be used for this if appropriate (it may not be - cifs, for example). The data will be stored encrypted in the local cache using the same manner of encryption as the data written to the server and the library will impose bounce buffering and RMW cycles as necessary. Per-Inode Context¶The network filesystem helper library needs a place to store a bit of state for its use on each netfs inode it is helping to manage. To this end, a context structure is defined: struct netfs_inode {
struct inode inode;
const struct netfs_request_ops *ops;
struct fscache_cookie * cache;
loff_t _remote_i_size;
unsigned long flags;
...
};
A network filesystem that wants to use netfslib must place one of these in its
inode wrapper struct my_inode {
struct netfs_inode netfs; /* Netfslib context and vfs inode */
...
};
This allows netfslib to find its state by using The structure contains the following fields that are of interest to the filesystem:
Inode Context Helper Functions¶To help deal with the per-inode context, a number helper functions are provided. Firstly, a function to perform basic initialisation on a context and set the operations table pointer: void netfs_inode_init(struct netfs_inode *ctx,
const struct netfs_request_ops *ops);
then a function to cast from the VFS inode structure to the netfs context: struct netfs_inode *netfs_inode(struct inode *inode); and finally, a function to get the cache cookie pointer from the context attached to an inode (or NULL if fscache is disabled): struct fscache_cookie *netfs_i_cookie(struct netfs_inode *ctx); Inode Locking¶A number of functions are provided to manage the locking of i_rwsem for I/O and to effectively extend it to provide more separate classes of exclusion: int netfs_start_io_read(struct inode *inode); void netfs_end_io_read(struct inode *inode); int netfs_start_io_write(struct inode *inode); void netfs_end_io_write(struct inode *inode); int netfs_start_io_direct(struct inode *inode); void netfs_end_io_direct(struct inode *inode); The exclusion breaks down into four separate classes:
Inode Writeback¶Netfslib will pin resources on an inode for future writeback (such as pinning use of an fscache cookie) when an inode is dirtied. However, this pinning needs careful management. To manage the pinning, the following sequence occurs:
To do the cleanup, netfslib provides a function to do the resource unpinning: int netfs_unpin_writeback(struct inode *inode, struct writeback_control *wbc); If the filesystem doesn’t need to do anything else, this may be set as a its
Further, if an inode is deleted, the filesystem’s write_inode method may not get called, so: void netfs_clear_inode_writeback(struct inode *inode, const void *aux); must be called from High-Level VFS API¶Netfslib provides a number of sets of API calls for the filesystem to delegate VFS operations to. Netfslib, in turn, will call out to the filesystem and the cache to negotiate I/O sizes, issue RPCs and provide places for it to intervene at various times. Unlocked Read/Write Iter¶The first API set is for the delegation of operations to netfslib when the filesystem is called through the standard VFS read/write_iter methods: ssize_t netfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter); ssize_t netfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from); ssize_t netfs_buffered_read_iter(struct kiocb *iocb, struct iov_iter *iter); ssize_t netfs_unbuffered_read_iter(struct kiocb *iocb, struct iov_iter *iter); ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from); They can be assigned directly to Pre-Locked Read/Write Iter¶The second API set is for the delegation of operations to netfslib when the filesystem is called through the standard VFS methods, but needs to do some other stuff before or after calling netfslib whilst still inside locked section (e.g. Ceph negotiating caps). The unbuffered read function is: ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *iter); This must not be assigned directly to There are three functions for writes: ssize_t netfs_buffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *from,
struct netfs_group *netfs_group);
ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
struct netfs_group *netfs_group);
ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *iter,
struct netfs_group *netfs_group);
These must not be assigned directly to The first two functions are for buffered writes; the first just adds some standard write checks and jumps to the second, but if the filesystem wants to do the checks itself, it can use the second directly. The third function is for unbuffered or DIO writes. On all three write functions, there is a writeback group pointer (which should be NULL if the filesystem doesn’t use this). Writeback groups are set on folios when they’re modified. If a folio to-be-modified is already marked with a different group, it is flushed first. The writeback API allows writing back of a specific group. Memory-Mapped I/O API¶An API for support of mmap()’d I/O is provided: vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_group); This allows the filesystem to delegate Monolithic Files API¶There is also a special API set for files for which the content must be read in a single RPC (and not written back) and is maintained as a monolithic blob (e.g. an AFS directory), though it can be stored and updated in the local cache: ssize_t netfs_read_single(struct inode *inode, struct file *file, struct iov_iter *iter);
void netfs_single_mark_inode_dirty(struct inode *inode);
int netfs_writeback_single(struct address_space *mapping,
struct writeback_control *wbc,
struct iov_iter *iter);
The first function reads from a file into the given buffer, reading from the cache in preference if the data is cached there; the second function allows the inode to be marked dirty, causing a later writeback; and the third function can be called from the writeback code to write the data to the cache, if there is one. The inode should be marked Clearing Stale Post-EOF Pagecache¶When a file is extended, data left in the pagecache past the old EOF by a write
through an mmap must not be exposed as file content. Netfslib clears this on
its own write paths, and exports a helper so a filesystem can do the same from
a resize path (truncate, setattr, fallocate and the like) that holds the
inode’s void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from,
uoff_t to);
This zeroes any such data within the This overlaps with High-Level VM API¶Netfslib also provides a number of sets of API calls for the filesystem to delegate VM operations to. Again, netfslib, in turn, will call out to the filesystem and the cache to negotiate I/O sizes, issue RPCs and provide places for it to intervene at various times: void netfs_readahead(struct readahead_control *);
int netfs_read_folio(struct file *, struct folio *);
int netfs_writepages(struct address_space *mapping,
struct writeback_control *wbc);
bool netfs_dirty_folio(struct address_space *mapping, struct folio *folio);
void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length);
bool netfs_release_folio(struct folio *folio, gfp_t gfp);
These are Deprecated PG_private_2 API¶There is also a deprecated function for filesystems that still use the
int netfs_write_begin(struct netfs_inode *inode, struct file *file,
struct address_space *mapping, loff_t pos, unsigned int len,
struct folio **_folio, void **_fsdata);
It uses the deprecated PG_private_2 flag and so should not be used. I/O Request API¶The I/O request API comprises a number of structures and a number of functions that the filesystem may need to use. Request Structure¶The request structure manages the request as a whole, holding some resources and state on behalf of the filesystem and tracking the collection of results: struct netfs_io_request {
enum netfs_io_origin origin;
struct inode *inode;
struct address_space *mapping;
struct netfs_group *group;
struct netfs_io_stream io_streams[];
void *netfs_priv;
void *netfs_priv2;
unsigned long long start;
unsigned long long len;
unsigned long long i_size;
unsigned int debug_id;
unsigned long flags;
...
};
Many of the fields are for internal use, but the fields shown here are of interest to the filesystem:
If the filesystem wants more private data than is afforded by this structure, then it should wrap it and provide its own allocator. Stream Structure¶A request is comprised of one or more parallel streams and each stream may be aimed at a different target. For read requests, only stream 0 is used. This can contain a mixture of
subrequests aimed at different sources. For write requests, stream 0 is used
for the server and stream 1 is used for the cache. For buffered writeback,
stream 0 is not enabled unless a normal dirty folio is encountered, at which
point -> The stream struct netfs_io_stream {
unsigned char stream_nr;
bool avail;
size_t sreq_max_len;
unsigned int sreq_max_segs;
unsigned int submit_extendable_to;
...
};
A number of members are available for access/use by the filesystem:
Subrequest Structure¶Individual units of I/O are managed by the subrequest structure. These represent slices of the overall request and run independently: struct netfs_io_subrequest {
struct netfs_io_request *rreq;
struct iov_iter io_iter;
unsigned long long start;
size_t len;
size_t transferred;
unsigned long flags;
short error;
unsigned short debug_index;
unsigned char stream_nr;
...
};
Each subrequest is expected to access a single source, though the library will handle falling back from one source type to another. The members are:
If necessary, the filesystem can get and put extra refs on the subrequest it is given: void netfs_get_subrequest(struct netfs_io_subrequest *subreq,
enum netfs_sreq_ref_trace what);
void netfs_put_subrequest(struct netfs_io_subrequest *subreq,
enum netfs_sreq_ref_trace what);
using netfs trace codes to indicate the reason. Care must be taken, however, as once control of the subrequest is returned to netfslib, the same subrequest can be reissued/retried. Filesystem Methods¶The filesystem sets a table of operations in struct netfs_request_ops {
mempool_t *request_pool;
mempool_t *subrequest_pool;
int (*init_request)(struct netfs_io_request *rreq, struct file *file);
void (*free_request)(struct netfs_io_request *rreq);
void (*free_subrequest)(struct netfs_io_subrequest *rreq);
void (*expand_readahead)(struct netfs_io_request *rreq);
int (*prepare_read)(struct netfs_io_subrequest *subreq);
void (*issue_read)(struct netfs_io_subrequest *subreq);
void (*done)(struct netfs_io_request *rreq);
void (*update_i_size)(struct inode *inode, loff_t i_size);
void (*post_modify)(struct inode *inode);
void (*begin_writeback)(struct netfs_io_request *wreq);
void (*prepare_write)(struct netfs_io_subrequest *subreq);
void (*issue_write)(struct netfs_io_subrequest *subreq);
void (*retry_request)(struct netfs_io_request *wreq,
struct netfs_io_stream *stream);
void (*invalidate_cache)(struct netfs_io_request *wreq);
};
The table starts with a pair of optional pointers to memory pools from which requests and subrequests can be allocated. If these are not given, netfslib has default pools that it will use instead. If the filesystem wraps the netfs structs in its own larger structs, then it will need to use its own pools. Netfslib will allocate directly from the pools. The methods defined in the table are:
Terminating a subrequest¶When a subrequest completes, there are a number of functions that the cache or subrequest can call to inform netfslib of the status change. One function is provided to terminate a write subrequest at the preparation stage and acts synchronously:
Note that -> The other functions are used for subrequests that got as far as being issued:
Local Cache API¶Netfslib provides a separate API for a local cache to implement, though it provides some somewhat similar routines to the filesystem request API. Firstly, the netfs_io_request object contains a place for the cache to hang its state: struct netfs_cache_resources {
const struct netfs_cache_ops *ops;
void *cache_priv;
void *cache_priv2;
unsigned int debug_id;
unsigned int inval_counter;
};
This contains an operations table pointer and two private pointers plus the
debug ID of the fscache cookie for tracing purposes and an invalidation counter
that is cranked by calls to The cache operation table looks like the following: struct netfs_cache_ops {
void (*end_operation)(struct netfs_cache_resources *cres);
void (*expand_readahead)(struct netfs_cache_resources *cres,
loff_t *_start, size_t *_len, loff_t i_size);
enum netfs_io_source (*prepare_read)(struct netfs_io_subrequest *subreq,
loff_t i_size);
int (*read)(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter,
bool seek_data,
netfs_io_terminated_t term_func,
void *term_func_priv);
void (*prepare_write_subreq)(struct netfs_io_subrequest *subreq);
void (*issue_write)(struct netfs_io_subrequest *subreq);
};
With a termination handler function pointer: typedef void (*netfs_io_terminated_t)(void *priv,
ssize_t transferred_or_error,
bool was_async);
The methods defined in the table are:
API Function Reference¶Parameters
Description Call this function before writing a folio to a local cache. Starting a second write before the first one finishes is not allowed. Note that this should no longer be used.
Parameters
Description Get the netfs lib inode context from the network filesystem’s inode. The
context Parameters
Description Read remote_i_size safely without the potential for tearing on 32-bit arches. NOTE in a 32bit arch with a preemptable kernel and an UP compile the i_size_read/write must be atomic with respect to the local cpu (unlike with preempt disabled), but they don’t need to be atomic with respect to other cpus like in true SMP (so they need either to either locally disable irq around the read or for example on x86 they can be still implemented as a cmpxchg8b without the need of the lock prefix). For SMP compiles and 64bit archs it makes no difference if preempt is enabled or not. Parameters
Description Read zero_point safely without the potential for tearing on 32-bit arches. NOTE in a 32bit arch with a preemptable kernel and an UP compile the i_size_read/write must be atomic with respect to the local cpu (unlike with preempt disabled), but they don’t need to be atomic with respect to other cpus like in true SMP (so they need either to either locally disable irq around the read or for example on x86 they can be still implemented as a cmpxchg8b without the need of the lock prefix). For SMP compiles and 64bit archs it makes no difference if preempt is enabled or not.
Parameters
Description Read remote_i_size and zero_point safely without the potential for tearing on 32-bit arches. NOTE in a 32bit arch with a preemptable kernel and an UP compile the i_size_read/write must be atomic with respect to the local cpu (unlike with preempt disabled), but they don’t need to be atomic with respect to other cpus like in true SMP (so they need either to either locally disable irq around the read or for example on x86 they can be still implemented as a cmpxchg8b without the need of the lock prefix). For SMP compiles and 64bit archs it makes no difference if preempt is enabled or not.
Parameters
Description Initialise the netfs library context struct. This is expected to follow on directly from the VFS inode struct.
Parameters
Description Inform the netfs lib that a file got resized so that it can adjust its state.
Parameters
Description Get the caching cookie (if enabled) from the network filesystem’s inode. Parameters
Description Wait for outstanding I/O requests of any type to complete. This is intended to be called from inode eviction routines. This makes sure that any resources held by those requests are cleaned up before we let the inode get cleaned up.
Parameters
Description Fulfil a readahead request by drawing data from the cache if possible, or the netfs if not. Space beyond the EOF is zero-filled. Multiple I/O requests from different sources will get munged together. If necessary, the readahead window can be expanded in either direction to a more convenient alighment for RPC efficiency or to make storage in the cache feasible. The calling netfs must initialise a netfs context contiguous to the vfs inode before calling this. This is usable whether or not caching is enabled. Parameters
Description Fulfil a read_folio request by drawing data from the cache if possible, or the netfs if not. Space beyond the EOF is zero-filled. Multiple I/O requests from different sources will get munged together. The calling netfs must initialise a netfs context contiguous to the vfs inode before calling this. This is usable whether or not caching is enabled.
Parameters
Description Pre-read data for a write-begin request by drawing data from the cache if possible, or the netfs if not. Space beyond the EOF is zero-filled. Multiple I/O requests from different sources will get munged together. The calling netfs must provide a table of operations, only one of which, issue_read, is mandatory. The The calling netfs must initialise a netfs context contiguous to the vfs inode before calling this. This is usable whether or not caching is enabled. Note that this should be considered deprecated and
Parameters
Description This is the -> The IOCB_NOWAIT flag in iocb->ki_flags indicates that -EAGAIN shall be returned when no data can be read without waiting for I/O requests to complete; it doesn’t prevent readahead. The IOCB_NOIO flag in iocb->ki_flags indicates that no new I/O requests shall be made for the read or for readahead. When no data can be read, -EAGAIN shall be returned. When readahead would be triggered, a partial, possibly empty read shall be returned. Return
Parameters
Description This is the -> The IOCB_NOWAIT flag in iocb->ki_flags indicates that -EAGAIN shall be returned when no data can be read without waiting for I/O requests to complete; it doesn’t prevent readahead. The IOCB_NOIO flag in iocb->ki_flags indicates that no new I/O requests shall be made for the read or for readahead. When no data can be read, -EAGAIN shall be returned. When readahead would be triggered, a partial, possibly empty read shall be returned. Return
|
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |