diff --git a/block/Kconfig b/block/Kconfig
index f1364d1c0d93..5e721678ea3d 100644
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -228,6 +228,33 @@ config BLOCK_HOLDER_DEPRECATED
 config BLK_MQ_STACKING
 	bool
 
+config BLK_CONT_ACT_BASED_IOPRIO
+	bool "Enable content activity based ioprio"
+	depends on LRU_GEN
+	default n
+	help
+	  This item enable the feature of adjust bio's priority by
+	  calculating its content's activity.
+	  This feature works as a hint of original bio_set_ioprio
+	  which means rt task get no change of its bio->bi_ioprio
+	  while other tasks have the opportunity to raise the ioprio
+	  if the bio take certain numbers of active pages.
+	  The file system should use this by modifying their buffered
+	  read/write/sync function to raise the bio->bi_ioprio before
+	  calling submit_bio or after bio_add_page/folio
+
+config BLK_CONT_ACT_BASED_IOPRIO_ITER_BIO
+	bool "Counting bio's activity by iterating bio's pages"
+	depends on BLK_CONT_ACT_BASED_IOPRIO
+	help
+	  The API under this config counts bio's activity by iterating the bio.
+
+config BLK_CONT_ACT_BASED_IOPRIO_ADD_PAGE
+	bool "Counting bio's activity when adding page or folio"
+	depends on BLK_CONT_ACT_BASED_IOPRIO && !BLK_CONT_ACT_BASED_IOPRIO_ITER_BIO
+	help
+	  The API under this config count activity during each call buy can't
+	   handle bvec_try_merge_page cases, please be sure you are ok with that.
 source "block/Kconfig.iosched"
 
 endif # BLOCK
diff --git a/block/bio.c b/block/bio.c
index 816d412c06e9..73916a6c319f 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1476,6 +1476,100 @@ void bio_set_pages_dirty(struct bio *bio)
 }
 EXPORT_SYMBOL_GPL(bio_set_pages_dirty);
 
+/*
+ * bio_set_active_ioprio() is helper function for fs to adjust the bio's ioprio via
+ * calculating the content's activity which measured from MGLRU.
+ * The file system should call this function before submit_bio for the buffered
+ * read/write/sync.
+ */
+#ifdef CONFIG_BLK_CONT_ACT_BASED_IOPRIO
+#ifdef CONFIG_BLK_CONT_ACT_BASED_IOPRIO_ITER_BIO
+void bio_set_active_ioprio(struct bio *bio)
+{
+	struct bio_vec bv;
+	struct bvec_iter iter;
+	struct page *page;
+	int class, level, hint;
+	int activity = 0;
+	int cnt = 0;
+
+	class = IOPRIO_PRIO_CLASS(bio->bi_ioprio);
+	level = IOPRIO_PRIO_LEVEL(bio->bi_ioprio);
+	hint = IOPRIO_PRIO_HINT(bio->bi_ioprio);
+	/*apply legacy ioprio policy on RT task*/
+	if (task_is_realtime(current)) {
+		bio->bi_ioprio = IOPRIO_PRIO_VALUE_HINT(IOPRIO_CLASS_RT, level, hint);
+		return;
+	}
+	bio_for_each_bvec(bv, bio, iter) {
+		page = bv.bv_page;
+		activity += PageWorkingset(page) ? 1 : 0;
+		cnt++;
+		if (activity > bio->bi_vcnt / 2) {
+			class = IOPRIO_CLASS_RT;
+			break;
+		} else if (activity > bio->bi_vcnt / 4) {
+			/*
+			 * all itered pages are all active so far
+			 * then raise to RT directly
+			 */
+			if (activity == cnt) {
+				class = IOPRIO_CLASS_RT;
+				break;
+			} else
+				class = max(IOPRIO_PRIO_CLASS(get_current_ioprio()),
+						IOPRIO_CLASS_BE);
+		}
+	}
+	if (!class && activity > cnt / 2)
+		class = IOPRIO_CLASS_RT;
+	else if (!class && activity > cnt / 4)
+		class = max(IOPRIO_PRIO_CLASS(get_current_ioprio()), IOPRIO_CLASS_BE);
+
+	bio->bi_ioprio = IOPRIO_PRIO_VALUE_HINT(class, level, hint);
+}
+void bio_set_active_ioprio_folio(struct bio *bio, struct folio *folio) {}
+void bio_set_active_ioprio_page(struct bio *bio, struct page *page) {}
+#endif
+#ifdef CONFIG_BLK_CONT_ACT_BASED_IOPRIO_ADD_PAGE
+/*
+ * bio_set_active_ioprio_page/folio are helper functions for counting
+ * the bio's activity during each all. However, it can't handle the
+ * scenario of bvec_try_merge_page. The submitter can use them if there
+ * is no such case in the system(block size < page size)
+ */
+void bio_set_active_ioprio_page(struct bio *bio, struct page *page)
+{
+	int class, level, hint;
+
+	class = IOPRIO_PRIO_CLASS(bio->bi_ioprio);
+	level = IOPRIO_PRIO_LEVEL(bio->bi_ioprio);
+	hint = IOPRIO_PRIO_HINT(bio->bi_ioprio);
+	bio->bi_cont_act += PageWorkingset(page) ? 1 : 0;
+
+	if (bio->bi_cont_act > bio->bi_vcnt / 2)
+		class = IOPRIO_CLASS_RT;
+	else if (bio->bi_cont_act > bio->bi_vcnt / 4)
+		class = max(IOPRIO_PRIO_CLASS(get_current_ioprio()), IOPRIO_CLASS_BE);
+
+	bio->bi_ioprio = IOPRIO_PRIO_VALUE_HINT(class, level, hint);
+}
+
+void bio_set_active_ioprio_folio(struct bio *bio, struct folio *folio)
+{
+	bio_set_active_ioprio_page(bio, &folio->page);
+}
+void bio_set_active_ioprio(struct bio *bio) {}
+#endif
+#else
+void bio_set_active_ioprio(struct bio *bio) {}
+void bio_set_active_ioprio_page(struct bio *bio, struct page *page) {}
+void bio_set_active_ioprio_folio(struct bio *bio, struct folio *folio) {}
+#endif
+EXPORT_SYMBOL_GPL(bio_set_active_ioprio);
+EXPORT_SYMBOL_GPL(bio_set_active_ioprio_page);
+EXPORT_SYMBOL_GPL(bio_set_active_ioprio_folio);
+
 /*
  * bio_check_pages_dirty() will check that all the BIO's pages are still dirty.
  * If they are, then fine.  If, however, some pages are clean then they must
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 41d417ee1349..35221ee3dd54 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -487,6 +487,9 @@ void bio_iov_bvec_set(struct bio *bio, struct iov_iter *iter);
 void __bio_release_pages(struct bio *bio, bool mark_dirty);
 extern void bio_set_pages_dirty(struct bio *bio);
 extern void bio_check_pages_dirty(struct bio *bio);
+extern void bio_set_active_ioprio(struct bio *bio);
+extern void bio_set_active_ioprio_folio(struct bio *bio, struct folio *folio);
+extern void bio_set_active_ioprio_page(struct bio *bio, struct page *page);
 
 extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
 			       struct bio *src, struct bvec_iter *src_iter);
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index d5c5e59ddbd2..a3a18b9a5168 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -314,7 +314,12 @@ struct bio {
 	struct bio_vec		*bi_io_vec;	/* the actual vec list */
 
 	struct bio_set		*bi_pool;
-
+#ifdef CONFIG_BLK_CONT_ACT_BASED_IOPRIO
+	/*
+	 * bi_cont_act record total activities of bi_io_vec->pages
+	 */
+	u64			bi_cont_act;
+#endif
 	/*
 	 * We can inline a number of vecs at the end of the bio, to avoid
 	 * double allocations for a small number of bio_vecs. This member
