Skip to content

Commit 7674a42

Browse files
committed
erofs: use struct lockref to replace handcrafted approach
Let's avoid the current handcrafted lockref although `struct lockref` inclusion usually increases extra 4 bytes with an explicit spinlock if CONFIG_DEBUG_SPINLOCK is off. Apart from the size difference, note that the meaning of refcount is also changed to active users. IOWs, it doesn't take an extra refcount for XArray tree insertion. I don't observe any significant performance difference at least on our cloud compute server but the new one indeed simplifies the overall codebase a bit. Signed-off-by: Gao Xiang <[email protected]> Reviewed-by: Yue Hu <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 7b4e372 commit 7674a42

File tree

3 files changed

+51
-88
lines changed

3 files changed

+51
-88
lines changed

fs/erofs/internal.h

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -208,46 +208,12 @@ enum {
208208
EROFS_ZIP_CACHE_READAROUND
209209
};
210210

211-
#define EROFS_LOCKED_MAGIC (INT_MIN | 0xE0F510CCL)
212-
213211
/* basic unit of the workstation of a super_block */
214212
struct erofs_workgroup {
215-
/* the workgroup index in the workstation */
216213
pgoff_t index;
217-
218-
/* overall workgroup reference count */
219-
atomic_t refcount;
214+
struct lockref lockref;
220215
};
221216

222-
static inline bool erofs_workgroup_try_to_freeze(struct erofs_workgroup *grp,
223-
int val)
224-
{
225-
preempt_disable();
226-
if (val != atomic_cmpxchg(&grp->refcount, val, EROFS_LOCKED_MAGIC)) {
227-
preempt_enable();
228-
return false;
229-
}
230-
return true;
231-
}
232-
233-
static inline void erofs_workgroup_unfreeze(struct erofs_workgroup *grp,
234-
int orig_val)
235-
{
236-
/*
237-
* other observers should notice all modifications
238-
* in the freezing period.
239-
*/
240-
smp_mb();
241-
atomic_set(&grp->refcount, orig_val);
242-
preempt_enable();
243-
}
244-
245-
static inline int erofs_wait_on_workgroup_freezed(struct erofs_workgroup *grp)
246-
{
247-
return atomic_cond_read_relaxed(&grp->refcount,
248-
VAL != EROFS_LOCKED_MAGIC);
249-
}
250-
251217
enum erofs_kmap_type {
252218
EROFS_NO_KMAP, /* don't map the buffer */
253219
EROFS_KMAP, /* use kmap_local_page() to map the buffer */
@@ -486,7 +452,7 @@ static inline void erofs_pagepool_add(struct page **pagepool, struct page *page)
486452
void erofs_release_pages(struct page **pagepool);
487453

488454
#ifdef CONFIG_EROFS_FS_ZIP
489-
int erofs_workgroup_put(struct erofs_workgroup *grp);
455+
void erofs_workgroup_put(struct erofs_workgroup *grp);
490456
struct erofs_workgroup *erofs_find_workgroup(struct super_block *sb,
491457
pgoff_t index);
492458
struct erofs_workgroup *erofs_insert_workgroup(struct super_block *sb,

fs/erofs/utils.c

Lines changed: 41 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* https://www.huawei.com/
55
*/
66
#include "internal.h"
7-
#include <linux/pagevec.h>
87

98
struct page *erofs_allocpage(struct page **pagepool, gfp_t gfp)
109
{
@@ -33,22 +32,21 @@ void erofs_release_pages(struct page **pagepool)
3332
/* global shrink count (for all mounted EROFS instances) */
3433
static atomic_long_t erofs_global_shrink_cnt;
3534

36-
static int erofs_workgroup_get(struct erofs_workgroup *grp)
35+
static bool erofs_workgroup_get(struct erofs_workgroup *grp)
3736
{
38-
int o;
37+
if (lockref_get_not_zero(&grp->lockref))
38+
return true;
3939

40-
repeat:
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;
40+
spin_lock(&grp->lockref.lock);
41+
if (__lockref_is_dead(&grp->lockref)) {
42+
spin_unlock(&grp->lockref.lock);
43+
return false;
44+
}
4745

48-
/* decrease refcount paired by erofs_workgroup_put */
49-
if (o == 1)
46+
if (!grp->lockref.count++)
5047
atomic_long_dec(&erofs_global_shrink_cnt);
51-
return 0;
48+
spin_unlock(&grp->lockref.lock);
49+
return true;
5250
}
5351

5452
struct erofs_workgroup *erofs_find_workgroup(struct super_block *sb,
@@ -61,7 +59,7 @@ struct erofs_workgroup *erofs_find_workgroup(struct super_block *sb,
6159
rcu_read_lock();
6260
grp = xa_load(&sbi->managed_pslots, index);
6361
if (grp) {
64-
if (erofs_workgroup_get(grp)) {
62+
if (!erofs_workgroup_get(grp)) {
6563
/* prefer to relax rcu read side */
6664
rcu_read_unlock();
6765
goto repeat;
@@ -80,11 +78,10 @@ struct erofs_workgroup *erofs_insert_workgroup(struct super_block *sb,
8078
struct erofs_workgroup *pre;
8179

8280
/*
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.
81+
* Bump up before making this visible to others for the XArray in order
82+
* to avoid potential UAF without serialized by xa_lock.
8683
*/
87-
atomic_inc(&grp->refcount);
84+
lockref_get(&grp->lockref);
8885

8986
repeat:
9087
xa_lock(&sbi->managed_pslots);
@@ -93,13 +90,13 @@ struct erofs_workgroup *erofs_insert_workgroup(struct super_block *sb,
9390
if (pre) {
9491
if (xa_is_err(pre)) {
9592
pre = ERR_PTR(xa_err(pre));
96-
} else if (erofs_workgroup_get(pre)) {
93+
} else if (!erofs_workgroup_get(pre)) {
9794
/* try to legitimize the current in-tree one */
9895
xa_unlock(&sbi->managed_pslots);
9996
cond_resched();
10097
goto repeat;
10198
}
102-
atomic_dec(&grp->refcount);
99+
lockref_put_return(&grp->lockref);
103100
grp = pre;
104101
}
105102
xa_unlock(&sbi->managed_pslots);
@@ -112,38 +109,34 @@ static void __erofs_workgroup_free(struct erofs_workgroup *grp)
112109
erofs_workgroup_free_rcu(grp);
113110
}
114111

115-
int erofs_workgroup_put(struct erofs_workgroup *grp)
112+
void erofs_workgroup_put(struct erofs_workgroup *grp)
116113
{
117-
int count = atomic_dec_return(&grp->refcount);
114+
if (lockref_put_or_lock(&grp->lockref))
115+
return;
118116

119-
if (count == 1)
117+
DBG_BUGON(__lockref_is_dead(&grp->lockref));
118+
if (grp->lockref.count == 1)
120119
atomic_long_inc(&erofs_global_shrink_cnt);
121-
else if (!count)
122-
__erofs_workgroup_free(grp);
123-
return count;
120+
--grp->lockref.count;
121+
spin_unlock(&grp->lockref.lock);
124122
}
125123

126124
static bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi,
127125
struct erofs_workgroup *grp)
128126
{
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;
127+
int free = false;
128+
129+
spin_lock(&grp->lockref.lock);
130+
if (grp->lockref.count)
131+
goto out;
136132

137133
/*
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.
134+
* Note that all cached pages should be detached before deleted from
135+
* the XArray. Otherwise some cached pages could be still attached to
136+
* the orphan old workgroup when the new one is available in the tree.
142137
*/
143-
if (erofs_try_to_free_all_cached_pages(sbi, grp)) {
144-
erofs_workgroup_unfreeze(grp, 1);
145-
return false;
146-
}
138+
if (erofs_try_to_free_all_cached_pages(sbi, grp))
139+
goto out;
147140

148141
/*
149142
* It's impossible to fail after the workgroup is freezed,
@@ -152,10 +145,13 @@ static bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi,
152145
*/
153146
DBG_BUGON(__xa_erase(&sbi->managed_pslots, grp->index) != grp);
154147

155-
/* last refcount should be connected with its managed pslot. */
156-
erofs_workgroup_unfreeze(grp, 0);
157-
__erofs_workgroup_free(grp);
158-
return true;
148+
lockref_mark_dead(&grp->lockref);
149+
free = true;
150+
out:
151+
spin_unlock(&grp->lockref.lock);
152+
if (free)
153+
__erofs_workgroup_free(grp);
154+
return free;
159155
}
160156

161157
static unsigned long erofs_shrink_workstation(struct erofs_sb_info *sbi,

fs/erofs/zdata.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ int erofs_try_to_free_all_cached_pages(struct erofs_sb_info *sbi,
641641

642642
DBG_BUGON(z_erofs_is_inline_pcluster(pcl));
643643
/*
644-
* refcount of workgroup is now freezed as 1,
644+
* refcount of workgroup is now freezed as 0,
645645
* therefore no need to worry about available decompression users.
646646
*/
647647
for (i = 0; i < pcl->pclusterpages; ++i) {
@@ -674,10 +674,11 @@ static bool z_erofs_cache_release_folio(struct folio *folio, gfp_t gfp)
674674
if (!folio_test_private(folio))
675675
return true;
676676

677-
if (!erofs_workgroup_try_to_freeze(&pcl->obj, 1))
678-
return false;
679-
680677
ret = false;
678+
spin_lock(&pcl->obj.lockref.lock);
679+
if (pcl->obj.lockref.count > 0)
680+
goto out;
681+
681682
DBG_BUGON(z_erofs_is_inline_pcluster(pcl));
682683
for (i = 0; i < pcl->pclusterpages; ++i) {
683684
if (pcl->compressed_bvecs[i].page == &folio->page) {
@@ -686,10 +687,10 @@ static bool z_erofs_cache_release_folio(struct folio *folio, gfp_t gfp)
686687
break;
687688
}
688689
}
689-
erofs_workgroup_unfreeze(&pcl->obj, 1);
690-
691690
if (ret)
692691
folio_detach_private(folio);
692+
out:
693+
spin_unlock(&pcl->obj.lockref.lock);
693694
return ret;
694695
}
695696

@@ -805,7 +806,7 @@ static int z_erofs_register_pcluster(struct z_erofs_decompress_frontend *fe)
805806
if (IS_ERR(pcl))
806807
return PTR_ERR(pcl);
807808

808-
atomic_set(&pcl->obj.refcount, 1);
809+
spin_lock_init(&pcl->obj.lockref.lock);
809810
pcl->algorithmformat = map->m_algorithmformat;
810811
pcl->length = 0;
811812
pcl->partial = true;

0 commit comments

Comments
 (0)