erofs: force inplace I/O under low memory scenario

Try to forcely use inplace I/O under low memory scenario
in order to avoid direct memory reclaim.

Signed-off-by: Gao Xiang <hsiangkao@aol.com>
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index fad80c9..4dd0a98 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -20,6 +20,8 @@
 enum z_erofs_cache_alloctype {
 	DONTALLOC,	/* don't allocate any cached pages */
 	DELAYEDALLOC,	/* delayed allocation (at the time of submitting io) */
+	TRYALLOC,	/* try to allocate or do in-place approach otherwise */
+			/* to prevent causing direct reclaim */
 };
 
 /*
@@ -171,6 +173,7 @@
 	struct page **pages = clt->compressedpages;
 	pgoff_t index = pcl->obj.index + (pages - pcl->compressed_pages);
 	bool standalone = true;
+	gfp_t gfp = mapping_gfp_constraint(mc, GFP_KERNEL) & ~__GFP_DIRECT_RECLAIM;
 
 	if (clt->mode < COLLECT_PRIMARY_FOLLOWED)
 		return;
@@ -178,6 +181,7 @@
 	for (; pages < pcl->compressed_pages + clusterpages; ++pages) {
 		struct page *page;
 		compressed_page_t t;
+		struct page *newpage = NULL;
 
 		/* the compressed page was loaded before */
 		if (READ_ONCE(*pages))
@@ -189,7 +193,21 @@
 			t = tag_compressed_page_justfound(page);
 		} else if (type == DELAYEDALLOC) {
 			t = tagptr_init(compressed_page_t, PAGE_UNALLOCATED);
+		} else if (type == TRYALLOC) {
+			gfp |= __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN;
+
+			if (!list_empty(pagepool)) {
+				newpage = lru_to_page(pagepool);
+				list_del(&newpage->lru);
+			} else {
+				newpage = alloc_page(gfp);
+			}
+			if (!newpage)
+				goto fallback_dontalloc;
+			newpage->mapping = Z_EROFS_MAPPING_PREALLOCATED;
+			t = tag_compressed_page_justfound(newpage);
 		} else {	/* DONTALLOC */
+fallback_dontalloc:
 			if (standalone)
 				clt->compressedpages = pages;
 			standalone = false;
@@ -199,8 +217,12 @@
 		if (!cmpxchg_relaxed(pages, NULL, tagptr_cast_ptr(t)))
 			continue;
 
-		if (page)
+		if (page) {
 			put_page(page);
+		} else if (newpage) {
+			newpage->mapping = NULL;
+			list_add(&newpage->lru, pagepool);
+		}
 	}
 
 	if (standalone)		/* downgrade to PRIMARY_FOLLOWED_NOINPLACE */
@@ -621,7 +643,8 @@
 
 	/* preload all compressed pages (maybe downgrade role if necessary) */
 	if (should_alloc_managed_pages(fe, sbi->cache_strategy, map->m_la))
-		cache_strategy = DELAYEDALLOC;
+		/* cache_strategy = DELAYEDALLOC; */
+		cache_strategy = TRYALLOC;
 	else
 		cache_strategy = DONTALLOC;
 
@@ -1043,6 +1066,11 @@
 		goto out;
 	}
 
+	if (mapping == Z_EROFS_MAPPING_PREALLOCATED) {
+		WRITE_ONCE(pcl->compressed_pages[nr], page);
+		goto out_to_managed_cache;
+	}
+
 	/*
 	 * unmanaged (file) pages are all locked solidly,
 	 * therefore it is impossible for `mapping' to be NULL.
@@ -1101,6 +1129,7 @@
 	}
 	if (nocache || !tocache)
 		goto out;
+out_to_managed_cache:
 	if (add_to_page_cache_lru(page, mc, index + nr, gfp)) {
 		page->mapping = Z_EROFS_MAPPING_STAGING;
 		goto out;
diff --git a/fs/erofs/zdata.h b/fs/erofs/zdata.h
index faf9501..400af91 100644
--- a/fs/erofs/zdata.h
+++ b/fs/erofs/zdata.h
@@ -10,6 +10,11 @@
 #include "internal.h"
 #include "zpvec.h"
 
+/*
+ *  - 0x6A110C8D ('pallocated', Z_EROFS_MAPPING_PREALLOCATED) -
+ * preallocated cached pages, and will be added into managed cache space
+ */
+#define Z_EROFS_MAPPING_PREALLOCATED	((void *)0x6A110C8D)
 #define Z_EROFS_NR_INLINE_PAGEVECS      3
 
 /*