diff --git a/commands/service/parse.c b/commands/service/parse.c index a84db8012..342080ba0 100644 --- a/commands/service/parse.c +++ b/commands/service/parse.c @@ -734,6 +734,7 @@ struct { "PROCCTL", VM_PROCCTL }, { "MAPCACHEPAGE", VM_MAPCACHEPAGE }, { "SETCACHEPAGE", VM_SETCACHEPAGE }, + { "CLEARCACHE", VM_CLEARCACHE }, { "VFS_MMAP", VM_VFS_MMAP }, { "VFS_REPLY", VM_VFS_REPLY }, { NULL, 0 }, diff --git a/etc/system.conf b/etc/system.conf index dc81ce5ce..950cd6b08 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -110,7 +110,7 @@ service mfs { ipc ALL_SYS; # All system ipc targets allowed system BASIC; # Only basic kernel calls allowed - vm MAPCACHEPAGE SETCACHEPAGE; + vm MAPCACHEPAGE SETCACHEPAGE CLEARCACHE; io NONE; # No I/O range allowed irq NONE; # No IRQ allowed sigmgr rs; # Signal manager is RS @@ -137,7 +137,7 @@ service ext2 { ipc ALL_SYS; # All system ipc targets allowed system BASIC; # Only basic kernel calls allowed - vm MAPCACHEPAGE SETCACHEPAGE; + vm MAPCACHEPAGE SETCACHEPAGE CLEARCACHE; io NONE; # No I/O range allowed irq NONE; # No IRQ allowed sigmgr rs; # Signal manager is RS @@ -150,7 +150,7 @@ service pfs { ipc ALL_SYS; # All system ipc targets allowed system BASIC; # Only basic kernel calls allowed - vm MAPCACHEPAGE SETCACHEPAGE; + vm MAPCACHEPAGE SETCACHEPAGE CLEARCACHE; io NONE; # No I/O range allowed irq NONE; # No IRQ allowed sigmgr rs; # Signal manager is RS diff --git a/include/minix/com.h b/include/minix/com.h index c7629f0b6..3847825f7 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -978,6 +978,9 @@ /* To VM: identify cache block in FS */ #define VM_SETCACHEPAGE (VM_RQ_BASE+27) +/* To VM: clear all cache blocks for a device */ +#define VM_CLEARCACHE (VM_RQ_BASE+28) + /* To VFS: fields for request from VM. */ # define VFS_VMCALL_REQ m10_i1 # define VFS_VMCALL_FD m10_i2 diff --git a/include/minix/vm.h b/include/minix/vm.h index a4388ddb3..f41cbf002 100644 --- a/include/minix/vm.h +++ b/include/minix/vm.h @@ -72,6 +72,8 @@ int vm_set_cacheblock(void *block, u32_t dev, u64_t dev_offset, void *vm_map_cacheblock(u32_t dev, u64_t dev_offset, u64_t ino, u64_t ino_offset, u32_t *flags, int blocksize); +int vm_clear_cache(u32_t dev); + /* flags for vm cache functions */ #define VMMC_FLAGS_LOCKED 0x01 /* someone is updating the flags; don't read/write */ #define VMMC_DIRTY 0x02 /* dirty buffer and it may not be evicted */ diff --git a/lib/libminixfs/cache.c b/lib/libminixfs/cache.c index 0f2f78ed6..1968918ed 100644 --- a/lib/libminixfs/cache.c +++ b/lib/libminixfs/cache.c @@ -577,6 +577,8 @@ void lmfs_invalidate( bp->data = NULL; } } + + vm_clear_cache(device); } /*===========================================================================* diff --git a/lib/libsys/vm_cache.c b/lib/libsys/vm_cache.c index ce844acf0..171ba39c8 100644 --- a/lib/libsys/vm_cache.c +++ b/lib/libsys/vm_cache.c @@ -60,3 +60,17 @@ int vm_set_cacheblock(void *block, u32_t dev, u64_t dev_offset, return vm_cachecall(&m, VM_SETCACHEPAGE, block, dev, dev_offset, ino, ino_offset, flags, blocksize); } + +int +vm_clear_cache(u32_t dev) +{ + message m; + + assert(dev != NO_DEV); + + memset(&m, 0, sizeof(m)); + + m.m_u.m_vmmcp.dev = dev; + + return _taskcall(VM_PROC_NR, VM_CLEARCACHE, &m); +} diff --git a/servers/ext2/mount.c b/servers/ext2/mount.c index f97edc062..c2d76ff1f 100644 --- a/servers/ext2/mount.c +++ b/servers/ext2/mount.c @@ -241,6 +241,9 @@ int fs_unmount() /* Close the device the file system lives on. */ bdev_close(fs_dev); + /* Throw all blocks out of the VM cache, to prevent corruption later. */ + lmfs_invalidate(fs_dev); + /* Finish off the unmount. */ superblock->s_dev = NO_DEV; unmountdone = TRUE; diff --git a/servers/mfs/mount.c b/servers/mfs/mount.c index 60a6b50f7..efdcb299b 100644 --- a/servers/mfs/mount.c +++ b/servers/mfs/mount.c @@ -187,6 +187,9 @@ int fs_unmount() /* Close the device the file system lives on. */ bdev_close(fs_dev); + /* Throw out blocks out of the VM cache, to prevent corruption later. */ + lmfs_invalidate(fs_dev); + /* Finish off the unmount. */ superblock.s_dev = NO_DEV; unmountdone = TRUE; diff --git a/servers/vm/cache.c b/servers/vm/cache.c index fd6be5ab3..6935357d0 100644 --- a/servers/vm/cache.c +++ b/servers/vm/cache.c @@ -304,6 +304,25 @@ int cache_freepages(int pages) return freed; } +/* + * Remove all pages that are associated with the given device. + */ +void +clear_cache_bydev(dev_t dev) +{ + struct cached_page *cp, *ncp; + int h; + + for (h = 0; h < HASHSIZE; h++) { + for (cp = cache_hash_bydev[h]; cp != NULL; cp = ncp) { + ncp = cp->hash_next_dev; + + if (cp->dev == dev) + rmcache(cp); + } + } +} + void get_stats_info(struct vm_stats_info *vsi) { vsi->vsi_cached = cached_pages; diff --git a/servers/vm/main.c b/servers/vm/main.c index c882cf068..58fe36a6c 100644 --- a/servers/vm/main.c +++ b/servers/vm/main.c @@ -485,6 +485,7 @@ void init_vm(void) /* Cache blocks. */ CALLMAP(VM_MAPCACHEPAGE, do_mapcache); CALLMAP(VM_SETCACHEPAGE, do_setcache); + CALLMAP(VM_CLEARCACHE, do_clearcache); /* getrusage */ CALLMAP(VM_GETRUSAGE, do_getrusage); diff --git a/servers/vm/mem_cache.c b/servers/vm/mem_cache.c index 674efe4c7..6a359d77c 100644 --- a/servers/vm/mem_cache.c +++ b/servers/vm/mem_cache.c @@ -238,3 +238,17 @@ do_setcache(message *msg) return OK; } +/* + * A file system wants to invalidate all pages belonging to a certain device. + */ +int +do_clearcache(message *msg) +{ + dev_t dev; + + dev = msg->m_u.m_vmmcp.dev; + + clear_cache_bydev(dev); + + return OK; +} diff --git a/servers/vm/proto.h b/servers/vm/proto.h index 5092a875f..995a99e6d 100644 --- a/servers/vm/proto.h +++ b/servers/vm/proto.h @@ -218,6 +218,7 @@ void shared_setsource(struct vir_region *vr, endpoint_t ep, struct vir_region *s /* mem_cache.c */ int do_mapcache(message *m); int do_setcache(message *m); +int do_clearcache(message *m); /* cache.c */ struct cached_page *find_cached_page_bydev(dev_t dev, u64_t dev_off, @@ -229,6 +230,7 @@ int cache_freepages(int pages); void get_stats_info(struct vm_stats_info *vsi); void cache_lru_touch(struct cached_page *hb); void rmcache(struct cached_page *cp); +void clear_cache_bydev(dev_t dev); /* vfs.c */ int vfs_request(int reqno, int fd, struct vmproc *vmp, u64_t offset, diff --git a/test/test72.c b/test/test72.c index 9c6ff3f90..4407fc239 100644 --- a/test/test72.c +++ b/test/test72.c @@ -224,16 +224,6 @@ panic(const char *fmt, ...) exit(1); } -int vm_forgetblock(u64_t id) -{ - return ENOSYS; -} - -void vm_forgetblocks(void) -{ - return; -} - int vm_info_stats(struct vm_stats_info *vsi) { @@ -274,6 +264,11 @@ void *vm_map_cacheblock(u32_t dev, u64_t dev_offset, return MAP_FAILED; } +int vm_clear_cache(u32_t dev) +{ + return 0; +} + int main(int argc, char *argv[]) { diff --git a/test/testvm.c b/test/testvm.c index d42d211a9..847f93e12 100644 --- a/test/testvm.c +++ b/test/testvm.c @@ -177,6 +177,8 @@ main(int argc, char *argv[]) writepipe(&info); + vm_clear_cache(MYDEV); + return 0; } diff --git a/test/testvm.conf b/test/testvm.conf index 24aed285e..ec42c0e25 100644 --- a/test/testvm.conf +++ b/test/testvm.conf @@ -1,7 +1,7 @@ service testvm { ipc ALL; # All system ipc targets allowed system BASIC; # Only basic kernel calls allowed - vm MAPCACHEPAGE SETCACHEPAGE; + vm MAPCACHEPAGE SETCACHEPAGE CLEARCACHE; io NONE; # No I/O range allowed irq NONE; # No IRQ allowed sigmgr rs; # Signal manager is RS