From: Gao Xiang <hsiangkao@xxxxxxxxxx> Hongyu reported "id != index" in z_erofs_onlinepage_fixup() with specific aarch64 environment easily, which wasn't shown before. After digging into that, I found that high 32 bits of page->private was set to 0xaaaaaaaa rather than 0 (due to z_erofs_onlinepage_init behavior with specific compiler options). Actually we only use low 32 bits to keep the page information since page->private is only 4 bytes on most 32-bit platforms. However z_erofs_onlinepage_fixup() uses the upper 32 bits by mistake. Let's fix it now. Reported-by: Hongyu Jin <hongyu.jin@xxxxxxxxxx> Fixes: 3883a79abd02 ("staging: erofs: introduce VLE decompression support") Cc: <stable@xxxxxxxxxxxxxxx> # 4.19+ Signed-off-by: Gao Xiang <hsiangkao@xxxxxxxxxx> --- fs/erofs/zdata.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/erofs/zdata.h b/fs/erofs/zdata.h index 7824f5563a55..92fbc0f0ba85 100644 --- a/fs/erofs/zdata.h +++ b/fs/erofs/zdata.h @@ -144,22 +144,24 @@ static inline void z_erofs_onlinepage_init(struct page *page) static inline void z_erofs_onlinepage_fixup(struct page *page, uintptr_t index, bool down) { - unsigned long *p, o, v, id; + union z_erofs_onlinepage_converter u; + int orig, orig_index, val; + repeat: - p = &page_private(page); - o = READ_ONCE(*p); + u.v = &page_private(page); + orig = atomic_read(u.o); - id = o >> Z_EROFS_ONLINEPAGE_INDEX_SHIFT; - if (id) { + orig_index = orig >> Z_EROFS_ONLINEPAGE_INDEX_SHIFT; + if (orig_index) { if (!index) return; - DBG_BUGON(id != index); + DBG_BUGON(orig_index != index); } - v = (index << Z_EROFS_ONLINEPAGE_INDEX_SHIFT) | - ((o & Z_EROFS_ONLINEPAGE_COUNT_MASK) + (unsigned int)down); - if (cmpxchg(p, o, v) != o) + val = (index << Z_EROFS_ONLINEPAGE_INDEX_SHIFT) | + ((orig & Z_EROFS_ONLINEPAGE_COUNT_MASK) + (unsigned int)down); + if (atomic_cmpxchg(u.o, orig, val) != orig) goto repeat; } -- 2.24.0