diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index d56151fe77d..fd24f34f120 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -229,6 +229,8 @@ struct super_operations { ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t); + int (*nr_cached_objects)(struct super_block *); + void (*free_cached_objects)(struct super_block *, int); }; All methods are called without any locks being held, unless otherwise @@ -301,6 +303,20 @@ or bottom half). quota_write: called by the VFS to write to filesystem quota file. + nr_cached_objects: called by the sb cache shrinking function for the + filesystem to return the number of freeable cached objects it contains. + Optional. + + free_cache_objects: called by the sb cache shrinking function for the + filesystem to scan the number of objects indicated to try to free them. + Optional, but any filesystem implementing this method needs to also + implement ->nr_cached_objects for it to be called correctly. + + We can't do anything with any errors that the filesystem might + encountered, hence the void return type. This will never be called if + the VM is trying to reclaim under GFP_NOFS conditions, hence this + method does not need to handle that situation itself. + Whoever sets up the inode is responsible for filling in the "i_op" field. This is a pointer to a "struct inode_operations" which describes the methods that can be performed on individual inodes. diff --git a/fs/super.c b/fs/super.c index 37a75410079..5101f054496 100644 --- a/fs/super.c +++ b/fs/super.c @@ -48,7 +48,8 @@ DEFINE_SPINLOCK(sb_lock); static int prune_super(struct shrinker *shrink, struct shrink_control *sc) { struct super_block *sb; - int count; + int fs_objects = 0; + int total_objects; sb = container_of(shrink, struct super_block, s_shrink); @@ -62,22 +63,42 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc) if (!grab_super_passive(sb)) return -1; + if (sb->s_op && sb->s_op->nr_cached_objects) + fs_objects = sb->s_op->nr_cached_objects(sb); + + total_objects = sb->s_nr_dentry_unused + + sb->s_nr_inodes_unused + fs_objects + 1; + if (sc->nr_to_scan) { - /* proportion the scan between the two caches */ - int total; + int dentries; + int inodes; - total = sb->s_nr_dentry_unused + sb->s_nr_inodes_unused + 1; - count = (sc->nr_to_scan * sb->s_nr_dentry_unused) / total; + /* proportion the scan between the caches */ + dentries = (sc->nr_to_scan * sb->s_nr_dentry_unused) / + total_objects; + inodes = (sc->nr_to_scan * sb->s_nr_inodes_unused) / + total_objects; + if (fs_objects) + fs_objects = (sc->nr_to_scan * fs_objects) / + total_objects; + /* + * prune the dcache first as the icache is pinned by it, then + * prune the icache, followed by the filesystem specific caches + */ + prune_dcache_sb(sb, dentries); + prune_icache_sb(sb, inodes); - /* prune dcache first as icache is pinned by it */ - prune_dcache_sb(sb, count); - prune_icache_sb(sb, sc->nr_to_scan - count); + if (fs_objects && sb->s_op->free_cached_objects) { + sb->s_op->free_cached_objects(sb, fs_objects); + fs_objects = sb->s_op->nr_cached_objects(sb); + } + total_objects = sb->s_nr_dentry_unused + + sb->s_nr_inodes_unused + fs_objects; } - count = ((sb->s_nr_dentry_unused + sb->s_nr_inodes_unused) / 100) - * sysctl_vfs_cache_pressure; + total_objects = (total_objects / 100) * sysctl_vfs_cache_pressure; drop_super(sb); - return count; + return total_objects; } /** diff --git a/include/linux/fs.h b/include/linux/fs.h index d7f35e90b84..1393742bba9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1655,6 +1655,8 @@ struct super_operations { ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t); #endif int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t); + int (*nr_cached_objects)(struct super_block *); + void (*free_cached_objects)(struct super_block *, int); }; /*