History

fuse: allow filesystems to have precise control over data cache

On networked filesystems file data can be changed externally. FUSE provides notification messages for filesystem to inform kernel that metadata or data region of a file needs to be invalidated in local page cache. That provides the basis for filesystem implementations to invalidate kernel cache explicitly based on observed filesystem-specific events. FUSE has also "automatic" invalidation mode(*) when the kernel automatically invalidates data cache of a file if it sees mtime change. It also automatically invalidates whole data cache of a file if it sees file size being changed. The automatic mode has corresponding capability - FUSE_AUTO_INVAL_DATA. However, due to probably historical reason, that capability controls only whether mtime change should be resulting in automatic invalidation or not. A change in file size always results in invalidating whole data cache of a file irregardless of whether FUSE_AUTO_INVAL_DATA was negotiated(+). The filesystem I write[1] represents data arrays stored in networked database as local files suitable for mmap. It is read-only filesystem - changes to data are committed externally via database interfaces and the filesystem only glues data into contiguous file streams suitable for mmap and traditional array processing. The files are big - starting from hundreds gigabytes and more. The files change regularly, and frequently by data being appended to their end. The size of files thus changes frequently. If a file was accessed locally and some part of its data got into page cache, we want that data to stay cached unless there is memory pressure, or unless corresponding part of the file was actually changed. However current FUSE behaviour - when it sees file size change - is to invalidate the whole file. The data cache of the file is thus completely lost even on small size change, and despite that the filesystem server is careful to accurately translate database changes into FUSE invalidation messages to kernel. Let's fix it: if a filesystem, through new FUSE_EXPLICIT_INVAL_DATA capability, indicates to kernel that it is fully responsible for data cache invalidation, then the kernel won't invalidate files data cache on size change and only truncate that cache to new size in case the size decreased. (*) see 72d0d248ca "fuse: add FUSE_AUTO_INVAL_DATA init flag", eed2179efe "fuse: invalidate inode mapping if mtime changes" (+) in writeback mode the kernel does not invalidate data cache on file size change, but neither it allows the filesystem to set the size due to external event (see 8373200b12 "fuse: Trust kernel i_size only") [1] https://lab.nexedi.com/kirr/wendelin.core/blob/a50f1d9f/wcfs/wcfs.go#L20

Kirill Smelkov committed 7 years ago

Commit ad2ba641 parent f2294483 files · +14 −3Browse files

Showing the changes to include/uapi/linux (1 of 3 files). Show all

Unified · Split

include/uapi/linux/fuse.h+6−1
@@ -125,6 +125,9 @@ · 124 unmodified lines
125125 *
126126 * 7.29
127127 * - add FUSE_NO_OPENDIR_SUPPORT flag
128+ *
129+ * 7.30
130+ * - add FUSE_EXPLICIT_INVAL_DATA
128131 */
129132
130133 #ifndef _LINUX_FUSE_H
@@ -160,7 +163,7 @@ · 29 unmodified lines
160163 #define FUSE_KERNEL_VERSION 7
161164
162165 /** Minor version number of this interface */
163−#define FUSE_KERNEL_MINOR_VERSION 29
166+#define FUSE_KERNEL_MINOR_VERSION 30
164167
165168 /** The node ID of the root inode */
166169 #define FUSE_ROOT_ID 1
@@ -263,6 +266,7 @@ struct fuse_file_lock { · 96 unmodified lines
263266 * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
264267 * FUSE_CACHE_SYMLINKS: cache READLINK responses
265268 * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
269+ * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request
266270 */
267271 #define FUSE_ASYNC_READ (1 << 0)
268272 #define FUSE_POSIX_LOCKS (1 << 1)
@@ -289,6 +293,7 @@ struct fuse_file_lock { · 20 unmodified lines
289293 #define FUSE_MAX_PAGES (1 << 22)
290294 #define FUSE_CACHE_SYMLINKS (1 << 23)
291295 #define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
296+#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
292297
293298 /**
294299 * CUSE INIT request/reply flags