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 three 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.
devdevice identifier to VM, and VM removes all blocks associated with that device from its cache.
There are three calls,
int vm_set_cacheblock(void *block, u32_t dev, u64_t dev_offset, u64_t ino, u64_t ino_offset, u32_t *flags, int blocksize);
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 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(u32_t dev, u64_t dev_offset, u64_t ino, u64_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_clear_cache(u32_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.