这部分主要对linux虚拟文件系统内核初始化部分做些补充。
关于shrinker,inode和dentry cache初始化阶段都需要注册自己的shrinker,用于缩减cache。两个操作原理类似。
shrinker数据结构介绍
- /*
- * A callback you can register to apply pressure to ageable caches.
- *
- * 'shrink' is passed a count 'nr_to_scan' and a 'gfpmask'. It should
- * look through the least-recently-used 'nr_to_scan' entries and
- * attempt to free them up. It should return the number of objects
- * which remain in the cache. If it returns -1, it means it cannot do
- * any scanning at this time (eg. there is a risk of deadlock).
- *
- * The 'gfpmask' refers to the allocation we are currently trying to
- * fulfil.
- *
- * Note that 'shrink' will be passed nr_to_scan == 0 when the VM is
- * querying the cache size, so a fastpath for that case is appropriate.
- */
- struct shrinker {
- int (*shrink)(int nr_to_scan, gfp_t gfp_mask);
- int seeks; /* seeks to recreate an obj */
- /* These are for internal use */
- struct list_head list;
- long nr; /* objs pending delete */
- };
1,注册inode cache shrinker
Start_kernel()->vfs_caches_init()->dcache_init()->register_shrinker(&dcache_shrinker);
- /*
- * Add a shrinker callback to be called from the vm
- */
- void register_shrinker(struct shrinker *shrinker)
- {
- shrinker->nr = 0;
- down_write(&shrinker_rwsem);
- list_add_tail(&shrinker->list, &shrinker_list);
- up_write(&shrinker_rwsem);
- }
其中相关的函数在这里定义。
- static struct shrinker dcache_shrinker = {
- .shrink = shrink_dcache_memory,
- .seeks = DEFAULT_SEEKS,
- };
- /*
- * Scan `nr' dentries and return the number which remain.
- *
- * We need to avoid reentering the filesystem if the caller is performing a
- * GFP_NOFS allocation attempt. One example deadlock is:
- *
- * ext2_new_block->getblk->GFP->shrink_dcache_memory->prune_dcache->
- * prune_one_dentry->dput->dentry_iput->iput->inode->i_sb->s_op->put_inode->
- * ext2_discard_prealloc->ext2_free_blocks->lock_super->DEADLOCK.
- *
- * In this case we return -1 to tell the caller that we baled.
- */
- static int shrink_dcache_memory(int nr, gfp_t gfp_mask)
- {
- if (nr) {
- if (!(gfp_mask & __GFP_FS))
- return -1;
- prune_dcache(nr);/*缩减指定大小的cache*/
- }
- return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
- }
- /**
- * prune_dcache - shrink the dcache
- * @count: number of entries to try to free
- *
- * Shrink the dcache. This is done when we need more memory, or simply when we
- * need to unmount something (at which point we need to unuse all dentries).
- *
- * This function may fail to free any resources if all the dentries are in use.
- */
- /*缩减dcache,count为释放的数量*/
- static void prune_dcache(int count)
- {
- struct super_block *sb;
- int w_count;
- int unused = dentry_stat.nr_unused;
- int prune_ratio;
- int pruned;
- if (unused == 0 || count == 0)
- return;
- spin_lock(&dcache_lock);
- restart:
- if (count >= unused)
- prune_ratio = 1;/*释放率*/
- else
- prune_ratio = unused / count;
- spin_lock(&sb_lock);
- list_for_each_entry(sb, &super_blocks, s_list) {
- if (sb->s_nr_dentry_unused == 0)
- continue;
- sb->s_count++;
- /* Now, we reclaim unused dentrins with fairness.
- * We reclaim them same percentage from each superblock.
- * We calculate number of dentries to scan on this sb
- * as follows, but the implementation is arranged to avoid
- * overflows:
- * number of dentries to scan on this sb =
- * count * (number of dentries on this sb /
- * number of dentries in the machine)
- */
- spin_unlock(&sb_lock);
- /*重新利用释放率计算释放量*/
- if (prune_ratio != 1)
- w_count = (sb->s_nr_dentry_unused / prune_ratio) + 1;
- else
- w_count = sb->s_nr_dentry_unused;
- pruned = w_count;
- /*
- * We need to be sure this filesystem isn't being unmounted,
- * otherwise we could race with generic_shutdown_super(), and
- * end up holding a reference to an inode while the filesystem
- * is unmounted. So we try to get s_umount, and make sure
- * s_root isn't NULL.
- */
- if (down_read_trylock(&sb->s_umount)) {
- if ((sb->s_root != NULL) &&
- (!list_empty(&sb->s_dentry_lru))) {
- spin_unlock(&dcache_lock);
- /*实际释放工作*/
- __shrink_dcache_sb(sb, &w_count,
- DCACHE_REFERENCED);
- pruned -= w_count;
- spin_lock(&dcache_lock);
- }
- up_read(&sb->s_umount);
- }
- spin_lock(&sb_lock);
- count -= pruned;
- /*
- * restart only when sb is no longer on the list and
- * we have more work to do.
- */
- if (__put_super_and_need_restart(sb) && count > 0) {
- spin_unlock(&sb_lock);
- goto restart;
- }
- }
- spin_unlock(&sb_lock);
- spin_unlock(&dcache_lock);
- }