forked from Minki/linux
60b3005011
The root cause is the race as follows: Thread #1 Thread #2(irq ctx) z_erofs_runqueue() struct z_erofs_decompressqueue io_A[]; submit bio A z_erofs_decompress_kickoff(,,1) z_erofs_decompressqueue_endio(bio A) z_erofs_decompress_kickoff(,,-1) spin_lock_irqsave() atomic_add_return() io_wait_event() -> pending_bios is already 0 [end of function] wake_up_locked(io_A[]) // crash Referenced backtrace in kernel 5.4: [ 10.129422] Unable to handle kernel paging request at virtual address eb0454a4 [ 10.364157] CPU: 0 PID: 709 Comm: getprop Tainted: G WC O 5.4.147-ab09225 #1 [ 11.556325] [<c01b33b8>] (__wake_up_common) from [<c01b3300>] (__wake_up_locked+0x40/0x48) [ 11.565487] [<c01b3300>] (__wake_up_locked) from [<c044c8d0>] (z_erofs_vle_unzip_kickoff+0x6c/0xc0) [ 11.575438] [<c044c8d0>] (z_erofs_vle_unzip_kickoff) from [<c044c854>] (z_erofs_vle_read_endio+0x16c/0x17c) [ 11.586082] [<c044c854>] (z_erofs_vle_read_endio) from [<c06a80e8>] (clone_endio+0xb4/0x1d0) [ 11.595428] [<c06a80e8>] (clone_endio) from [<c04a1280>] (blk_update_request+0x150/0x4dc) [ 11.604516] [<c04a1280>] (blk_update_request) from [<c06dea28>] (mmc_blk_cqe_complete_rq+0x144/0x15c) [ 11.614640] [<c06dea28>] (mmc_blk_cqe_complete_rq) from [<c04a5d90>] (blk_done_softirq+0xb0/0xcc) [ 11.624419] [<c04a5d90>] (blk_done_softirq) from [<c010242c>] (__do_softirq+0x184/0x56c) [ 11.633419] [<c010242c>] (__do_softirq) from [<c01051e8>] (irq_exit+0xd4/0x138) [ 11.641640] [<c01051e8>] (irq_exit) from [<c010c314>] (__handle_domain_irq+0x94/0xd0) [ 11.650381] [<c010c314>] (__handle_domain_irq) from [<c04fde70>] (gic_handle_irq+0x50/0xd4) [ 11.659641] [<c04fde70>] (gic_handle_irq) from [<c0101b70>] (__irq_svc+0x70/0xb0) Signed-off-by: Hongyu Jin <hongyu.jin@unisoc.com> Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com> Reviewed-by: Chao Yu <chao@kernel.org> Link: https://lore.kernel.org/r/20220401115527.4935-1-hongyu.jin.cn@gmail.com Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
202 lines
4.9 KiB
C
202 lines
4.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (C) 2018 HUAWEI, Inc.
|
|
* https://www.huawei.com/
|
|
*/
|
|
#ifndef __EROFS_FS_ZDATA_H
|
|
#define __EROFS_FS_ZDATA_H
|
|
|
|
#include "internal.h"
|
|
#include "zpvec.h"
|
|
|
|
#define Z_EROFS_PCLUSTER_MAX_PAGES (Z_EROFS_PCLUSTER_MAX_SIZE / PAGE_SIZE)
|
|
#define Z_EROFS_NR_INLINE_PAGEVECS 3
|
|
|
|
/*
|
|
* Structure fields follow one of the following exclusion rules.
|
|
*
|
|
* I: Modifiable by initialization/destruction paths and read-only
|
|
* for everyone else;
|
|
*
|
|
* L: Field should be protected by pageset lock;
|
|
*
|
|
* A: Field should be accessed / updated in atomic for parallelized code.
|
|
*/
|
|
struct z_erofs_collection {
|
|
struct mutex lock;
|
|
|
|
/* I: page offset of start position of decompression */
|
|
unsigned short pageofs;
|
|
|
|
/* L: maximum relative page index in pagevec[] */
|
|
unsigned short nr_pages;
|
|
|
|
/* L: total number of pages in pagevec[] */
|
|
unsigned int vcnt;
|
|
|
|
union {
|
|
/* L: inline a certain number of pagevecs for bootstrap */
|
|
erofs_vtptr_t pagevec[Z_EROFS_NR_INLINE_PAGEVECS];
|
|
|
|
/* I: can be used to free the pcluster by RCU. */
|
|
struct rcu_head rcu;
|
|
};
|
|
};
|
|
|
|
#define Z_EROFS_PCLUSTER_FULL_LENGTH 0x00000001
|
|
#define Z_EROFS_PCLUSTER_LENGTH_BIT 1
|
|
|
|
/*
|
|
* let's leave a type here in case of introducing
|
|
* another tagged pointer later.
|
|
*/
|
|
typedef void *z_erofs_next_pcluster_t;
|
|
|
|
struct z_erofs_pcluster {
|
|
struct erofs_workgroup obj;
|
|
struct z_erofs_collection primary_collection;
|
|
|
|
/* A: point to next chained pcluster or TAILs */
|
|
z_erofs_next_pcluster_t next;
|
|
|
|
/* A: lower limit of decompressed length and if full length or not */
|
|
unsigned int length;
|
|
|
|
/* I: page offset of inline compressed data */
|
|
unsigned short pageofs_in;
|
|
|
|
union {
|
|
/* I: physical cluster size in pages */
|
|
unsigned short pclusterpages;
|
|
|
|
/* I: tailpacking inline compressed size */
|
|
unsigned short tailpacking_size;
|
|
};
|
|
|
|
/* I: compression algorithm format */
|
|
unsigned char algorithmformat;
|
|
|
|
/* A: compressed pages (can be cached or inplaced pages) */
|
|
struct page *compressed_pages[];
|
|
};
|
|
|
|
#define z_erofs_primarycollection(pcluster) (&(pcluster)->primary_collection)
|
|
|
|
/* let's avoid the valid 32-bit kernel addresses */
|
|
|
|
/* the chained workgroup has't submitted io (still open) */
|
|
#define Z_EROFS_PCLUSTER_TAIL ((void *)0x5F0ECAFE)
|
|
/* the chained workgroup has already submitted io */
|
|
#define Z_EROFS_PCLUSTER_TAIL_CLOSED ((void *)0x5F0EDEAD)
|
|
|
|
#define Z_EROFS_PCLUSTER_NIL (NULL)
|
|
|
|
struct z_erofs_decompressqueue {
|
|
struct super_block *sb;
|
|
atomic_t pending_bios;
|
|
z_erofs_next_pcluster_t head;
|
|
|
|
union {
|
|
struct completion done;
|
|
struct work_struct work;
|
|
} u;
|
|
};
|
|
|
|
static inline bool z_erofs_is_inline_pcluster(struct z_erofs_pcluster *pcl)
|
|
{
|
|
return !pcl->obj.index;
|
|
}
|
|
|
|
static inline unsigned int z_erofs_pclusterpages(struct z_erofs_pcluster *pcl)
|
|
{
|
|
if (z_erofs_is_inline_pcluster(pcl))
|
|
return 1;
|
|
return pcl->pclusterpages;
|
|
}
|
|
|
|
#define Z_EROFS_ONLINEPAGE_COUNT_BITS 2
|
|
#define Z_EROFS_ONLINEPAGE_COUNT_MASK ((1 << Z_EROFS_ONLINEPAGE_COUNT_BITS) - 1)
|
|
#define Z_EROFS_ONLINEPAGE_INDEX_SHIFT (Z_EROFS_ONLINEPAGE_COUNT_BITS)
|
|
|
|
/*
|
|
* waiters (aka. ongoing_packs): # to unlock the page
|
|
* sub-index: 0 - for partial page, >= 1 full page sub-index
|
|
*/
|
|
typedef atomic_t z_erofs_onlinepage_t;
|
|
|
|
/* type punning */
|
|
union z_erofs_onlinepage_converter {
|
|
z_erofs_onlinepage_t *o;
|
|
unsigned long *v;
|
|
};
|
|
|
|
static inline unsigned int z_erofs_onlinepage_index(struct page *page)
|
|
{
|
|
union z_erofs_onlinepage_converter u;
|
|
|
|
DBG_BUGON(!PagePrivate(page));
|
|
u.v = &page_private(page);
|
|
|
|
return atomic_read(u.o) >> Z_EROFS_ONLINEPAGE_INDEX_SHIFT;
|
|
}
|
|
|
|
static inline void z_erofs_onlinepage_init(struct page *page)
|
|
{
|
|
union {
|
|
z_erofs_onlinepage_t o;
|
|
unsigned long v;
|
|
/* keep from being unlocked in advance */
|
|
} u = { .o = ATOMIC_INIT(1) };
|
|
|
|
set_page_private(page, u.v);
|
|
smp_wmb();
|
|
SetPagePrivate(page);
|
|
}
|
|
|
|
static inline void z_erofs_onlinepage_fixup(struct page *page,
|
|
uintptr_t index, bool down)
|
|
{
|
|
union z_erofs_onlinepage_converter u = { .v = &page_private(page) };
|
|
int orig, orig_index, val;
|
|
|
|
repeat:
|
|
orig = atomic_read(u.o);
|
|
orig_index = orig >> Z_EROFS_ONLINEPAGE_INDEX_SHIFT;
|
|
if (orig_index) {
|
|
if (!index)
|
|
return;
|
|
|
|
DBG_BUGON(orig_index != index);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static inline void z_erofs_onlinepage_endio(struct page *page)
|
|
{
|
|
union z_erofs_onlinepage_converter u;
|
|
unsigned int v;
|
|
|
|
DBG_BUGON(!PagePrivate(page));
|
|
u.v = &page_private(page);
|
|
|
|
v = atomic_dec_return(u.o);
|
|
if (!(v & Z_EROFS_ONLINEPAGE_COUNT_MASK)) {
|
|
set_page_private(page, 0);
|
|
ClearPagePrivate(page);
|
|
if (!PageError(page))
|
|
SetPageUptodate(page);
|
|
unlock_page(page);
|
|
}
|
|
erofs_dbg("%s, page %p value %x", __func__, page, atomic_read(u.o));
|
|
}
|
|
|
|
#define Z_EROFS_VMAP_ONSTACK_PAGES \
|
|
min_t(unsigned int, THREAD_SIZE / 8 / sizeof(struct page *), 96U)
|
|
#define Z_EROFS_VMAP_GLOBAL_PAGES 2048
|
|
|
|
#endif
|