On this page
VFS Advanced
Coverage: page cache interaction with VFS → inode state machine → file locking (POSIX/OFD) → fsync/fdatasync guarantees → sendfile/splice/vmsplice zero-copy Kernel versions: 2.6 ~ 6.x
Page Cache and VFS Interaction
Relationship between inode and address_space
// inode->i_mapping points to the page cache for this file
// This is an address_space structure that manages all cached pages for this file in memory
// Most inode->i_mapping point to the embedded inode->i_data:
;
// For raw reads/writes on block devices:
;
inode State Machine
// include/linux/fs.h
// inode lifecycle:
// alloc_inode() → I_NEW
// insert_inode_locked() → added to inode hash
// unlock_new_inode() → clear I_NEW, wake up waiters
// ... active period ...
// iput() → i_count == 0 → evict() → I_FREEING → destroy_inode()
File Locking
POSIX Lock (fcntl)
// POSIX advisory/mandatory locks
;
; // non-blocking
; // blocking wait
; // query
// Implementation: fs/locks.c
// Locks are attached to inode->i_flctx (file_lock_context)
// Conflict detection: posix_locks_conflict() → traverse conflict chain
// Process closes fd → automatically releases all POSIX locks (regardless of fork relationship)
OFD Lock (Open File Description, 3.15+)
// Problem with POSIX locks: different fds of the same process cannot hold locks independently
// OFD lock solves this: locks are attached to the "open file description" (file), not the process
;
// fork scenario:
// POSIX: child process inherits parent's locks (because same PID)
// OFD: child process does not inherit (different file, not necessarily)
FLOCK (BSD Lock)
; // shared
; // exclusive
; // unlock
// Differences from POSIX locks:
// FLOCK is attached to the file (not inode, not process)
// Multiple flock calls on the same file are automatically associated
// fork inherits flock (because file is shared)
// Advisory only (no mandatory locks)
fsync / fdatasync: Durability Guarantees
// fs/sync.c
→ →
├─ // 1. Write back dirty page cache
│ └─ →
│ → for each dirty folio → a_ops->
│ └─ // wait for writeback completion
│
└─
└─ f_op-> // 2. Filesystem: write journal / force flush
→ ext4: jbd2 commit transaction + blkdev_issue_flush
→ XFS: force log flush + flush
→ NFS: COMMIT operation
// fdatasync (FDATASYNC): only flushes data part (does not flush i_atime/i_mtime)
// → vfs_fsync_range(file, start, end, 1)
// → filesystem can skip timestamp synchronization
Zero-Copy: sendfile / splice / vmsplice
// sendfile(): file → socket, bypasses user space
→
└─ → pipe buffer ← file content
└─ → pipe buffer → socket
// splice(): pipe transfer between two fds (kernel space)
; // fd → pipe
; // pipe → fd
// vmsplice(): user space memory → pipe (used with splice)
;
// user memory pages are "gifted" to the kernel (no longer user-space)
// → pipe references these pages → splice to socket → zero-copy to NIC
DMA Zero-Copy Path
Debugging
# inode cache statistics
# View process file locks
# Trace fsync behavior
References and Extensions
- Kernel Documentation:
Documentation/filesystems/locks.rst,Documentation/filesystems/vfs.rst - LWN:
- "Better file locking with OFD locks" (lwn.net/Articles/586904/)
- "Splice and sendfile" (lwn.net/Articles/178199/)
- Source Files:
fs/locks.c— POSIX/OFD/FLOCK implementationfs/sync.c— fsync/fdatasyncfs/splice.c— splice/sendfile/vmsplicefs/inode.c— inode lifecycle
Keywords: VFS, inode state, POSIX lock, OFD lock, FLOCK, fsync, fdatasync, sendfile, splice, zero-copy