The secondary block cache is a mechanism to utilize all available system memory as a disk cache. This functionality has to be in VM because it must be able to free the cache blocks if memory is needed for anything else, without blocking on it. I.e. it cannot rely on any other process to first free memory.
It is intended to be used by filesystem processes to store blocks it evicts from its own cache, hence the name 'secondary cache' and the use of the word 'block' and most of the time.
Blocks are identified by a (dev, dev_off)
pair. This pair uniquely
identifies a cache block in VM. These numbers have meaning to VM too:
dev is the full device number (i.e. major together with minor) of the
device it resides on, and dev_off is the offset from the minor device.
Furthermore each block has an inode and an inode offset associated with it. If the block is a file data block, this inode number must be a number uniquely identifying this file. Otherwise the inode number must be VMC_NO_INODE and the inode offset is irrelevant. The inode metadata is informational, they needn't be unique in the VM cache.
Logically there are four operations:
(dev, dev_off)
pair is required and is unique; a possible existing block with that ID will be removed from the cache.(dev, dev_off)
pair to VM, together with further metadata. If the block exists, VM maps the cache block in and returns the address to the caller. VM updates its notion of the inode and inode offset of the block.(dev, dev_off)
pair to VM, along with a block size. If the block exists, VM throws it out of the cache.dev
device identifier to VM, and VM removes all blocks associated with that device from its cache.
There are four calls, vm_set_cacheblock
, vm_map_cacheblock
, vm_forget_cacheblock
, and vm_clear_cache
.
int vm_set_cacheblock(void *block, dev_t dev, off_t dev_offset, ino_t ino, off_t ino_offset, u32_t *flags, int blocksize, int setflags);
This call indicates a new block to VM. If the block is not inode data, ino must be VMC_NO_INODE. flags points to a 32-bit flags field that is a mask of VMMC_* values. This is currently unused but it is to be expected that in the future, VM will evict blocks not marked by VMMC_BLOCK_LOCKED or VMMC_DIRTY, so callers must set these when in use or dirty, respectively.
The setflags
field should generally be set to zero, but the caller may supply VMSC_ONCE
to indicate that the block should be used only for immediate use to map in a mmap'ed page, and not be cached beyond that. The VMSC_ONCE
flag prevents cached blocks from going stale and is used by file systems that do not have a block cache and do not track updates to file contents.
The caller has allocated memory for block and filled it with the correct data already.
The call returns OK or an error code.
void *vm_map_cacheblock(dev_t dev, off_t dev_offset, ino_t ino, off_t ino_offset, u32_t *flags, int blocksize);
This call requests the block identified by the device number and offset to be mapped in. It returns an address on success or MAP_FAILED on failure (e.g. block not found). VM updates its notion of the inode metadata with the parameters if the block was already in the cache.
int vm_forget_cacheblock(dev_t dev, off_t dev_offset, int blocksize);
This call requests that the block identified by the device number and offset be thrown out of the VM cache. Any previous inode association for this block is therefore broken as well. The call returns OK on success, even if no matching block was found. An error code is returned if the given parameter are invalid.
int vm_clear_cache(dev_t dev);
This call requests that VM forget all blocks associated with the given device. This call should be used (directly or indirectly) by file systems when 1) they get a REQ_FLUSH request from VFS, and 2) when they successfully unmount. This ensures that no stale blocks remain in VM, which could cause corruption upon recall later.