cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

utils.c (6895B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2018 HUAWEI, Inc.
      4 *             https://www.huawei.com/
      5 */
      6#include "internal.h"
      7#include <linux/pagevec.h>
      8
      9struct page *erofs_allocpage(struct page **pagepool, gfp_t gfp)
     10{
     11	struct page *page = *pagepool;
     12
     13	if (page) {
     14		DBG_BUGON(page_ref_count(page) != 1);
     15		*pagepool = (struct page *)page_private(page);
     16	} else {
     17		page = alloc_page(gfp);
     18	}
     19	return page;
     20}
     21
     22void erofs_release_pages(struct page **pagepool)
     23{
     24	while (*pagepool) {
     25		struct page *page = *pagepool;
     26
     27		*pagepool = (struct page *)page_private(page);
     28		put_page(page);
     29	}
     30}
     31
     32#ifdef CONFIG_EROFS_FS_ZIP
     33/* global shrink count (for all mounted EROFS instances) */
     34static atomic_long_t erofs_global_shrink_cnt;
     35
     36static int erofs_workgroup_get(struct erofs_workgroup *grp)
     37{
     38	int o;
     39
     40repeat:
     41	o = erofs_wait_on_workgroup_freezed(grp);
     42	if (o <= 0)
     43		return -1;
     44
     45	if (atomic_cmpxchg(&grp->refcount, o, o + 1) != o)
     46		goto repeat;
     47
     48	/* decrease refcount paired by erofs_workgroup_put */
     49	if (o == 1)
     50		atomic_long_dec(&erofs_global_shrink_cnt);
     51	return 0;
     52}
     53
     54struct erofs_workgroup *erofs_find_workgroup(struct super_block *sb,
     55					     pgoff_t index)
     56{
     57	struct erofs_sb_info *sbi = EROFS_SB(sb);
     58	struct erofs_workgroup *grp;
     59
     60repeat:
     61	rcu_read_lock();
     62	grp = xa_load(&sbi->managed_pslots, index);
     63	if (grp) {
     64		if (erofs_workgroup_get(grp)) {
     65			/* prefer to relax rcu read side */
     66			rcu_read_unlock();
     67			goto repeat;
     68		}
     69
     70		DBG_BUGON(index != grp->index);
     71	}
     72	rcu_read_unlock();
     73	return grp;
     74}
     75
     76struct erofs_workgroup *erofs_insert_workgroup(struct super_block *sb,
     77					       struct erofs_workgroup *grp)
     78{
     79	struct erofs_sb_info *const sbi = EROFS_SB(sb);
     80	struct erofs_workgroup *pre;
     81
     82	/*
     83	 * Bump up a reference count before making this visible
     84	 * to others for the XArray in order to avoid potential
     85	 * UAF without serialized by xa_lock.
     86	 */
     87	atomic_inc(&grp->refcount);
     88
     89repeat:
     90	xa_lock(&sbi->managed_pslots);
     91	pre = __xa_cmpxchg(&sbi->managed_pslots, grp->index,
     92			   NULL, grp, GFP_NOFS);
     93	if (pre) {
     94		if (xa_is_err(pre)) {
     95			pre = ERR_PTR(xa_err(pre));
     96		} else if (erofs_workgroup_get(pre)) {
     97			/* try to legitimize the current in-tree one */
     98			xa_unlock(&sbi->managed_pslots);
     99			cond_resched();
    100			goto repeat;
    101		}
    102		atomic_dec(&grp->refcount);
    103		grp = pre;
    104	}
    105	xa_unlock(&sbi->managed_pslots);
    106	return grp;
    107}
    108
    109static void  __erofs_workgroup_free(struct erofs_workgroup *grp)
    110{
    111	atomic_long_dec(&erofs_global_shrink_cnt);
    112	erofs_workgroup_free_rcu(grp);
    113}
    114
    115int erofs_workgroup_put(struct erofs_workgroup *grp)
    116{
    117	int count = atomic_dec_return(&grp->refcount);
    118
    119	if (count == 1)
    120		atomic_long_inc(&erofs_global_shrink_cnt);
    121	else if (!count)
    122		__erofs_workgroup_free(grp);
    123	return count;
    124}
    125
    126static bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi,
    127					   struct erofs_workgroup *grp)
    128{
    129	/*
    130	 * If managed cache is on, refcount of workgroups
    131	 * themselves could be < 0 (freezed). In other words,
    132	 * there is no guarantee that all refcounts > 0.
    133	 */
    134	if (!erofs_workgroup_try_to_freeze(grp, 1))
    135		return false;
    136
    137	/*
    138	 * Note that all cached pages should be unattached
    139	 * before deleted from the XArray. Otherwise some
    140	 * cached pages could be still attached to the orphan
    141	 * old workgroup when the new one is available in the tree.
    142	 */
    143	if (erofs_try_to_free_all_cached_pages(sbi, grp)) {
    144		erofs_workgroup_unfreeze(grp, 1);
    145		return false;
    146	}
    147
    148	/*
    149	 * It's impossible to fail after the workgroup is freezed,
    150	 * however in order to avoid some race conditions, add a
    151	 * DBG_BUGON to observe this in advance.
    152	 */
    153	DBG_BUGON(__xa_erase(&sbi->managed_pslots, grp->index) != grp);
    154
    155	/* last refcount should be connected with its managed pslot.  */
    156	erofs_workgroup_unfreeze(grp, 0);
    157	__erofs_workgroup_free(grp);
    158	return true;
    159}
    160
    161static unsigned long erofs_shrink_workstation(struct erofs_sb_info *sbi,
    162					      unsigned long nr_shrink)
    163{
    164	struct erofs_workgroup *grp;
    165	unsigned int freed = 0;
    166	unsigned long index;
    167
    168	xa_lock(&sbi->managed_pslots);
    169	xa_for_each(&sbi->managed_pslots, index, grp) {
    170		/* try to shrink each valid workgroup */
    171		if (!erofs_try_to_release_workgroup(sbi, grp))
    172			continue;
    173		xa_unlock(&sbi->managed_pslots);
    174
    175		++freed;
    176		if (!--nr_shrink)
    177			return freed;
    178		xa_lock(&sbi->managed_pslots);
    179	}
    180	xa_unlock(&sbi->managed_pslots);
    181	return freed;
    182}
    183
    184/* protected by 'erofs_sb_list_lock' */
    185static unsigned int shrinker_run_no;
    186
    187/* protects the mounted 'erofs_sb_list' */
    188static DEFINE_SPINLOCK(erofs_sb_list_lock);
    189static LIST_HEAD(erofs_sb_list);
    190
    191void erofs_shrinker_register(struct super_block *sb)
    192{
    193	struct erofs_sb_info *sbi = EROFS_SB(sb);
    194
    195	mutex_init(&sbi->umount_mutex);
    196
    197	spin_lock(&erofs_sb_list_lock);
    198	list_add(&sbi->list, &erofs_sb_list);
    199	spin_unlock(&erofs_sb_list_lock);
    200}
    201
    202void erofs_shrinker_unregister(struct super_block *sb)
    203{
    204	struct erofs_sb_info *const sbi = EROFS_SB(sb);
    205
    206	mutex_lock(&sbi->umount_mutex);
    207	/* clean up all remaining workgroups in memory */
    208	erofs_shrink_workstation(sbi, ~0UL);
    209
    210	spin_lock(&erofs_sb_list_lock);
    211	list_del(&sbi->list);
    212	spin_unlock(&erofs_sb_list_lock);
    213	mutex_unlock(&sbi->umount_mutex);
    214}
    215
    216static unsigned long erofs_shrink_count(struct shrinker *shrink,
    217					struct shrink_control *sc)
    218{
    219	return atomic_long_read(&erofs_global_shrink_cnt);
    220}
    221
    222static unsigned long erofs_shrink_scan(struct shrinker *shrink,
    223				       struct shrink_control *sc)
    224{
    225	struct erofs_sb_info *sbi;
    226	struct list_head *p;
    227
    228	unsigned long nr = sc->nr_to_scan;
    229	unsigned int run_no;
    230	unsigned long freed = 0;
    231
    232	spin_lock(&erofs_sb_list_lock);
    233	do {
    234		run_no = ++shrinker_run_no;
    235	} while (run_no == 0);
    236
    237	/* Iterate over all mounted superblocks and try to shrink them */
    238	p = erofs_sb_list.next;
    239	while (p != &erofs_sb_list) {
    240		sbi = list_entry(p, struct erofs_sb_info, list);
    241
    242		/*
    243		 * We move the ones we do to the end of the list, so we stop
    244		 * when we see one we have already done.
    245		 */
    246		if (sbi->shrinker_run_no == run_no)
    247			break;
    248
    249		if (!mutex_trylock(&sbi->umount_mutex)) {
    250			p = p->next;
    251			continue;
    252		}
    253
    254		spin_unlock(&erofs_sb_list_lock);
    255		sbi->shrinker_run_no = run_no;
    256
    257		freed += erofs_shrink_workstation(sbi, nr - freed);
    258
    259		spin_lock(&erofs_sb_list_lock);
    260		/* Get the next list element before we move this one */
    261		p = p->next;
    262
    263		/*
    264		 * Move this one to the end of the list to provide some
    265		 * fairness.
    266		 */
    267		list_move_tail(&sbi->list, &erofs_sb_list);
    268		mutex_unlock(&sbi->umount_mutex);
    269
    270		if (freed >= nr)
    271			break;
    272	}
    273	spin_unlock(&erofs_sb_list_lock);
    274	return freed;
    275}
    276
    277static struct shrinker erofs_shrinker_info = {
    278	.scan_objects = erofs_shrink_scan,
    279	.count_objects = erofs_shrink_count,
    280	.seeks = DEFAULT_SEEKS,
    281};
    282
    283int __init erofs_init_shrinker(void)
    284{
    285	return register_shrinker(&erofs_shrinker_info);
    286}
    287
    288void erofs_exit_shrinker(void)
    289{
    290	unregister_shrinker(&erofs_shrinker_info);
    291}
    292#endif	/* !CONFIG_EROFS_FS_ZIP */