diff --git a/fs/ssdfs/peb_read_thread.c b/fs/ssdfs/peb_read_thread.c
index f6a5b67612af..317eef078521 100644
--- a/fs/ssdfs/peb_read_thread.c
+++ b/fs/ssdfs/peb_read_thread.c
@@ -245,6 +245,2294 @@ int ssdfs_read_blk2off_table_fragment(struct ssdfs_peb_info *pebi,
  *                          READ THREAD FUNCTIONALITY                         *
  ******************************************************************************/
 
+/*
+ * __ssdfs_peb_read_log_footer() - read log's footer
+ * @fsi: file system info object
+ * @pebi: PEB object
+ * @page_off: log's starting page
+ * @desc: footer's descriptor
+ * @log_bytes: pointer on value of bytes in the log [out]
+ *
+ * This function tries to read log's footer.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-ENOMEM     - fail to allocate memory.
+ * %-ENODATA    - valid footer is not found.
+ */
+static
+int __ssdfs_peb_read_log_footer(struct ssdfs_fs_info *fsi,
+				struct ssdfs_peb_info *pebi,
+				u16 page_off,
+				struct ssdfs_metadata_descriptor *desc,
+				u32 *log_bytes)
+{
+	struct ssdfs_signature *magic = NULL;
+	struct ssdfs_partial_log_header *plh_hdr = NULL;
+	struct ssdfs_log_footer *footer = NULL;
+	u16 footer_off;
+	u32 bytes_off;
+	struct page *page;
+	void *kaddr;
+	size_t read_bytes;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!fsi || !pebi || !pebi->pebc || !pebi->pebc->parent_si);
+	BUG_ON(!desc || !log_bytes);
+
+	SSDFS_DBG("seg %llu, peb_id %llu, page_off %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, page_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*log_bytes = U32_MAX;
+
+	bytes_off = le32_to_cpu(desc->offset);
+	footer_off = bytes_off / fsi->pagesize;
+
+	page = ssdfs_page_array_grab_page(&pebi->cache, footer_off);
+	if (unlikely(IS_ERR_OR_NULL(page))) {
+		SSDFS_ERR("fail to grab page: index %u\n",
+			  footer_off);
+		return -ENOMEM;
+	}
+
+	kaddr = kmap_local_page(page);
+
+	if (PageUptodate(page) || PageDirty(page))
+		goto check_footer_magic;
+
+	err = ssdfs_aligned_read_buffer(fsi, pebi->peb_id,
+					bytes_off,
+					(u8 *)kaddr,
+					PAGE_SIZE,
+					&read_bytes);
+	if (unlikely(err))
+		goto fail_read_footer;
+	else if (unlikely(read_bytes != PAGE_SIZE)) {
+		err = -ERANGE;
+		goto fail_read_footer;
+	}
+
+	SetPageUptodate(page);
+
+check_footer_magic:
+	magic = (struct ssdfs_signature *)kaddr;
+
+	if (!is_ssdfs_magic_valid(magic)) {
+		err = -ENODATA;
+		goto fail_read_footer;
+	}
+
+	if (is_ssdfs_partial_log_header_magic_valid(magic)) {
+		plh_hdr = SSDFS_PLH(kaddr);
+		*log_bytes = le32_to_cpu(plh_hdr->log_bytes);
+	} else if (__is_ssdfs_log_footer_magic_valid(magic)) {
+		footer = SSDFS_LF(kaddr);
+		*log_bytes = le32_to_cpu(footer->log_bytes);
+	} else {
+		err = -ENODATA;
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("log footer is corrupted: "
+			  "peb %llu, page_off %u\n",
+			  pebi->peb_id, page_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+		goto fail_read_footer;
+	}
+
+fail_read_footer:
+	kunmap_local(kaddr);
+	ssdfs_unlock_page(page);
+	ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("page %p, count %d\n",
+		  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (err == -ENODATA) {
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("valid footer is not detected: "
+			  "seg_id %llu, peb_id %llu, "
+			  "page_off %u\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  footer_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+		return err;
+	} else if (unlikely(err)) {
+		SSDFS_ERR("fail to read footer: "
+			  "seg %llu, peb %llu, "
+			  "pages_off %u, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  footer_off,
+			  err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * __ssdfs_peb_read_log_header() - read log's header
+ * @fsi: file system info object
+ * @pebi: PEB object
+ * @page_off: log's starting page
+ * @log_bytes: pointer on value of bytes in the log [out]
+ *
+ * This function tries to read the log's header.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-ENOMEM     - fail to allocate memory.
+ * %-ENODATA    - valid footer is not found.
+ */
+static
+int __ssdfs_peb_read_log_header(struct ssdfs_fs_info *fsi,
+				struct ssdfs_peb_info *pebi,
+				u16 page_off,
+				u32 *log_bytes)
+{
+	struct ssdfs_signature *magic = NULL;
+	struct ssdfs_segment_header *seg_hdr = NULL;
+	struct ssdfs_partial_log_header *pl_hdr = NULL;
+	struct ssdfs_metadata_descriptor *desc = NULL;
+	struct page *page;
+	void *kaddr;
+	size_t read_bytes;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!fsi || !pebi || !pebi->pebc || !pebi->pebc->parent_si);
+	BUG_ON(!log_bytes);
+
+	SSDFS_DBG("seg %llu, peb_id %llu, page_off %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, page_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*log_bytes = U32_MAX;
+
+	page = ssdfs_page_array_grab_page(&pebi->cache, page_off);
+	if (unlikely(IS_ERR_OR_NULL(page))) {
+		SSDFS_ERR("fail to grab page: index %u\n",
+			  page_off);
+		return -ENOMEM;
+	}
+
+	kaddr = kmap_local_page(page);
+
+	if (PageUptodate(page) || PageDirty(page))
+		goto check_header_magic;
+
+	err = ssdfs_aligned_read_buffer(fsi, pebi->peb_id,
+					page_off * PAGE_SIZE,
+					(u8 *)kaddr,
+					PAGE_SIZE,
+					&read_bytes);
+	if (unlikely(err))
+		goto fail_read_log_header;
+	else if (unlikely(read_bytes != PAGE_SIZE)) {
+		err = -ERANGE;
+		goto fail_read_log_header;
+	}
+
+	SetPageUptodate(page);
+
+check_header_magic:
+	magic = (struct ssdfs_signature *)kaddr;
+
+	if (!is_ssdfs_magic_valid(magic)) {
+		err = -ENODATA;
+		goto fail_read_log_header;
+	}
+
+	if (__is_ssdfs_segment_header_magic_valid(magic)) {
+		seg_hdr = SSDFS_SEG_HDR(kaddr);
+
+		err = ssdfs_check_segment_header(fsi, seg_hdr,
+						 false);
+		if (unlikely(err)) {
+			err = -ENODATA;
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("log header is corrupted: "
+				  "seg %llu, peb %llu, page_off %u\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id,
+				  page_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+			goto fail_read_log_header;
+		}
+
+		desc = &seg_hdr->desc_array[SSDFS_LOG_FOOTER_INDEX];
+
+		err = __ssdfs_peb_read_log_footer(fsi, pebi, page_off,
+						   desc, log_bytes);
+		if (err == -ENODATA) {
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("fail to read footer: "
+				  "seg %llu, peb %llu, page_off %u, "
+				  "err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id,
+				  page_off,
+				  err);
+#endif /* CONFIG_SSDFS_DEBUG */
+			goto fail_read_log_header;
+		} else if (unlikely(err)) {
+			SSDFS_ERR("fail to read footer: "
+				  "seg %llu, peb %llu, page_off %u, "
+				  "err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id,
+				  page_off,
+				  err);
+			goto fail_read_log_header;
+		}
+	} else if (is_ssdfs_partial_log_header_magic_valid(magic)) {
+		pl_hdr = SSDFS_PLH(kaddr);
+
+		err = ssdfs_check_partial_log_header(fsi, pl_hdr,
+						     false);
+		if (unlikely(err)) {
+			err = -ENODATA;
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("partial log header is corrupted: "
+				  "seg %llu, peb %llu, page_off %u\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id,
+				  page_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+			goto fail_read_log_header;
+		}
+
+		desc = &pl_hdr->desc_array[SSDFS_LOG_FOOTER_INDEX];
+
+		if (ssdfs_pl_has_footer(pl_hdr)) {
+			err = __ssdfs_peb_read_log_footer(fsi, pebi, page_off,
+							  desc, log_bytes);
+			if (err == -ENODATA) {
+#ifdef CONFIG_SSDFS_DEBUG
+				SSDFS_DBG("fail to read footer: "
+					  "seg %llu, peb %llu, page_off %u, "
+					  "err %d\n",
+					  pebi->pebc->parent_si->seg_id,
+					  pebi->peb_id,
+					  page_off,
+					  err);
+#endif /* CONFIG_SSDFS_DEBUG */
+				goto fail_read_log_header;
+			} else if (unlikely(err)) {
+				SSDFS_ERR("fail to read footer: "
+					  "seg %llu, peb %llu, page_off %u, "
+					  "err %d\n",
+					  pebi->pebc->parent_si->seg_id,
+					  pebi->peb_id,
+					  page_off,
+					  err);
+				goto fail_read_log_header;
+			}
+		} else
+			*log_bytes = le32_to_cpu(pl_hdr->log_bytes);
+	} else {
+		err = -ENODATA;
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("log header is corrupted: "
+			  "seg %llu, peb %llu, page_off %u\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  page_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+		goto fail_read_log_header;
+	}
+
+fail_read_log_header:
+	kunmap_local(kaddr);
+	ssdfs_unlock_page(page);
+	ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("page %p, count %d\n",
+		  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (err == -ENODATA) {
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("valid header is not detected: "
+			  "seg_id %llu, peb_id %llu, page_off %u\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  page_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+		return err;
+	} else if (unlikely(err)) {
+		SSDFS_ERR("fail to read checked log header: "
+			  "seg %llu, peb %llu, "
+			  "pages_off %u, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  page_off, err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_read_all_log_headers() - read all PEB's log headers
+ * @pebi: pointer on PEB object
+ * @req: read request
+ *
+ * This function tries to read all headers and footers of
+ * the PEB's logs.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-ENOMEM     - fail to allocate memory.
+ */
+static
+int ssdfs_peb_read_all_log_headers(struct ssdfs_peb_info *pebi,
+				   struct ssdfs_segment_request *req)
+{
+	struct ssdfs_fs_info *fsi;
+	u32 log_bytes = U32_MAX;
+	u32 page_off;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !pebi->pebc->parent_si);
+	BUG_ON(!pebi->pebc->parent_si->fsi);
+	BUG_ON(!req);
+
+	SSDFS_DBG("seg %llu, peb_index %u, "
+		  "class %#x, cmd %#x, type %#x, "
+		  "ino %llu, logical_offset %llu, data_bytes %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->pebc->peb_index,
+		  req->private.class, req->private.cmd, req->private.type,
+		  req->extent.ino, req->extent.logical_offset,
+		  req->extent.data_bytes);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	page_off = 0;
+
+	do {
+		u32 pages_per_log;
+
+		err = __ssdfs_peb_read_log_header(fsi, pebi, page_off,
+						  &log_bytes);
+		if (err == -ENODATA)
+			return 0;
+		else if (unlikely(err)) {
+			SSDFS_ERR("fail to read log header: "
+				  "seg %llu, peb %llu, page_off %u, "
+				  "err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id,
+				  page_off,
+				  err);
+			return err;
+		}
+
+#ifdef CONFIG_SSDFS_DEBUG
+		BUG_ON(log_bytes >= U32_MAX);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		pages_per_log = log_bytes + fsi->pagesize - 1;
+		pages_per_log /= fsi->pagesize;
+		page_off += pages_per_log;
+	} while (page_off < fsi->pages_per_peb);
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_read_src_all_log_headers() - read all source PEB's log headers
+ * @pebi: pointer on PEB object
+ * @req: read request
+ *
+ * This function tries to read all headers and footers of
+ * the source PEB's logs.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-ENOMEM     - fail to allocate memory.
+ */
+static
+int ssdfs_peb_read_src_all_log_headers(struct ssdfs_peb_container *pebc,
+					struct ssdfs_segment_request *req)
+{
+	struct ssdfs_peb_info *pebi;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebc || !pebc->parent_si || !pebc->parent_si->fsi);
+	BUG_ON(!req);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	down_read(&pebc->lock);
+
+	pebi = pebc->src_peb;
+	if (!pebi) {
+		SSDFS_WARN("source PEB is NULL\n");
+		err = -ERANGE;
+		goto finish_read_src_all_log_headers;
+	}
+
+#ifdef CONFIG_SSDFS_TRACK_API_CALL
+	SSDFS_ERR("seg_id %llu, peb_index %u, peb_id %llu\n",
+		  pebc->parent_si->seg_id,
+		  pebc->peb_index,
+		  pebi->peb_id);
+#else
+	SSDFS_DBG("seg_id %llu, peb_index %u, peb_id %llu\n",
+		  pebc->parent_si->seg_id,
+		  pebc->peb_index,
+		  pebi->peb_id);
+#endif /* CONFIG_SSDFS_TRACK_API_CALL */
+
+	err = ssdfs_peb_read_all_log_headers(pebi, req);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to read the log's headers: "
+			  "peb_id %llu, peb_index %u, err %d\n",
+			  pebi->peb_id, pebi->peb_index, err);
+		goto finish_read_src_all_log_headers;
+	}
+
+finish_read_src_all_log_headers:
+	up_read(&pebc->lock);
+
+#ifdef CONFIG_SSDFS_TRACK_API_CALL
+	SSDFS_ERR("finished: err %d\n", err);
+#else
+	SSDFS_DBG("finished: err %d\n", err);
+#endif /* CONFIG_SSDFS_TRACK_API_CALL */
+
+	return err;
+}
+
+/*
+ * ssdfs_peb_read_dst_all_log_headers() - read all dst PEB's log headers
+ * @pebi: pointer on PEB object
+ * @req: read request
+ *
+ * This function tries to read all headers and footers of
+ * the destination PEB's logs.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-ENOMEM     - fail to allocate memory.
+ */
+static
+int ssdfs_peb_read_dst_all_log_headers(struct ssdfs_peb_container *pebc,
+					struct ssdfs_segment_request *req)
+{
+	struct ssdfs_peb_info *pebi;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebc || !pebc->parent_si || !pebc->parent_si->fsi);
+	BUG_ON(!req);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	down_read(&pebc->lock);
+
+	pebi = pebc->dst_peb;
+	if (!pebi) {
+		SSDFS_WARN("destination PEB is NULL\n");
+		err = -ERANGE;
+		goto finish_read_dst_all_log_headers;
+	}
+
+#ifdef CONFIG_SSDFS_TRACK_API_CALL
+	SSDFS_ERR("seg_id %llu, peb_index %u, peb_id %llu\n",
+		  pebc->parent_si->seg_id,
+		  pebc->peb_index,
+		  pebi->peb_id);
+#else
+	SSDFS_DBG("seg_id %llu, peb_index %u, peb_id %llu\n",
+		  pebc->parent_si->seg_id,
+		  pebc->peb_index,
+		  pebi->peb_id);
+#endif /* CONFIG_SSDFS_TRACK_API_CALL */
+
+	err = ssdfs_peb_read_all_log_headers(pebi, req);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to read the log's headers: "
+			  "peb_id %llu, peb_index %u, err %d\n",
+			  pebi->peb_id, pebi->peb_index, err);
+		goto finish_read_dst_all_log_headers;
+	}
+
+finish_read_dst_all_log_headers:
+	up_read(&pebc->lock);
+
+#ifdef CONFIG_SSDFS_TRACK_API_CALL
+	SSDFS_ERR("finished: err %d\n", err);
+#else
+	SSDFS_DBG("finished: err %d\n", err);
+#endif /* CONFIG_SSDFS_TRACK_API_CALL */
+
+	return err;
+}
+
+/*
+ * ssdfs_peb_get_log_pages_count() - determine count of pages in the log
+ * @fsi: file system info object
+ * @pebi: PEB object
+ * @env: init environment [in | out]
+ *
+ * This function reads segment header of the first log in
+ * segment and to retrieve log_pages field. Also it initilizes
+ * current and previous PEB migration IDs.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_get_log_pages_count(struct ssdfs_fs_info *fsi,
+				  struct ssdfs_peb_info *pebi,
+				  struct ssdfs_read_init_env *env)
+{
+	struct ssdfs_signature *magic;
+	struct page *page;
+	size_t hdr_buf_size = sizeof(struct ssdfs_segment_header);
+	u32 log_pages;
+	u32 pages_off = 0;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!fsi || !env || !env->log_hdr);
+
+	SSDFS_DBG("peb %llu, env %p\n", pebi->peb_id, env);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	page = ssdfs_page_array_get_page_locked(&pebi->cache, 0);
+	if (IS_ERR_OR_NULL(page)) {
+		err = ssdfs_read_checked_segment_header(fsi,
+							pebi->peb_id,
+							0,
+							env->log_hdr,
+							false);
+		if (err) {
+			SSDFS_ERR("fail to read checked segment header: "
+				  "peb %llu, err %d\n",
+				  pebi->peb_id, err);
+			return err;
+		}
+	} else {
+		ssdfs_memcpy_from_page(env->log_hdr, 0, hdr_buf_size,
+					page, 0, PAGE_SIZE,
+					hdr_buf_size);
+
+		ssdfs_unlock_page(page);
+		ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("page %p, count %d\n",
+			  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+	}
+
+	magic = (struct ssdfs_signature *)env->log_hdr;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	if (!is_ssdfs_magic_valid(magic)) {
+		SSDFS_ERR("valid magic is not detected\n");
+		return -ERANGE;
+	}
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (__is_ssdfs_segment_header_magic_valid(magic)) {
+		struct ssdfs_segment_header *seg_hdr;
+
+		seg_hdr = SSDFS_SEG_HDR(env->log_hdr);
+		log_pages = le16_to_cpu(seg_hdr->log_pages);
+		env->log_pages = log_pages;
+		env->cur_migration_id =
+			seg_hdr->peb_migration_id[SSDFS_CUR_MIGRATING_PEB];
+		env->prev_migration_id =
+			seg_hdr->peb_migration_id[SSDFS_PREV_MIGRATING_PEB];
+	} else {
+		SSDFS_ERR("log header is corrupted: "
+			  "peb %llu, pages_off %u\n",
+			  pebi->peb_id, pages_off);
+		return -ERANGE;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	if (fsi->pages_per_peb % log_pages) {
+		SSDFS_WARN("fsi->pages_per_peb %u, log_pages %u\n",
+			   fsi->pages_per_peb, log_pages);
+	}
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (log_pages > fsi->pages_per_peb) {
+		SSDFS_ERR("log_pages %u > fsi->pages_per_peb %u\n",
+			  log_pages, fsi->pages_per_peb);
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_find_last_partial_log() - find the last partial log
+ * @fsi: file system info object
+ * @pebi: pointer on PEB object
+ * @env: init environment [in|out]
+ * @new_log_start_page: pointer on the new log's start page [out]
+ *
+ * This function tries to find the last partial log
+ * in the PEB's cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ */
+static
+int ssdfs_find_last_partial_log(struct ssdfs_fs_info *fsi,
+				struct ssdfs_peb_info *pebi,
+				struct ssdfs_read_init_env *env,
+				u16 *new_log_start_page)
+{
+	struct ssdfs_signature *magic = NULL;
+	struct ssdfs_segment_header *seg_hdr = NULL;
+	struct ssdfs_partial_log_header *pl_hdr = NULL;
+	struct ssdfs_log_footer *footer = NULL;
+	struct page *page;
+	void *kaddr;
+	size_t hdr_buf_size = sizeof(struct ssdfs_segment_header);
+	u32 byte_offset, page_offset;
+	unsigned long last_page_idx;
+	int i;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!fsi || !pebi || !pebi->pebc || !env);
+	BUG_ON(!new_log_start_page);
+
+	SSDFS_DBG("seg %llu, peb %llu, peb_index %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, pebi->peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*new_log_start_page = U16_MAX;
+
+	last_page_idx = ssdfs_page_array_get_last_page_index(&pebi->cache);
+
+	if (last_page_idx >= SSDFS_PAGE_ARRAY_INVALID_LAST_PAGE) {
+		SSDFS_ERR("empty page array: last_page_idx %lu\n",
+			  last_page_idx);
+		return -ERANGE;
+	}
+
+	if (last_page_idx >= fsi->pages_per_peb) {
+		SSDFS_ERR("corrupted page array: "
+			  "last_page_idx %lu, fsi->pages_per_peb %u\n",
+			  last_page_idx, fsi->pages_per_peb);
+		return -ERANGE;
+	}
+
+	for (i = (int)last_page_idx; i >= 0; i--) {
+		page = ssdfs_page_array_get_page_locked(&pebi->cache, i);
+		if (IS_ERR_OR_NULL(page)) {
+			if (page == NULL) {
+				SSDFS_ERR("fail to get page: "
+					  "index %d\n",
+					  i);
+				return -ERANGE;
+			} else {
+				err = PTR_ERR(page);
+
+				if (err == -ENOENT)
+					continue;
+				else {
+					SSDFS_ERR("fail to get page: "
+						  "index %d, err %d\n",
+						  i, err);
+					return err;
+				}
+			}
+		}
+
+		kaddr = kmap_local_page(page);
+		ssdfs_memcpy(env->log_hdr, 0, hdr_buf_size,
+			     kaddr, 0, PAGE_SIZE,
+			     hdr_buf_size);
+		ssdfs_memcpy(env->footer, 0, hdr_buf_size,
+			     kaddr, 0, PAGE_SIZE,
+			     hdr_buf_size);
+		kunmap_local(kaddr);
+		ssdfs_unlock_page(page);
+		ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("page_index %d, page %p, count %d\n",
+			  i, page, page_ref_count(page));
+
+		SSDFS_DBG("PAGE DUMP: cur_page %u\n",
+			  i);
+		kaddr = kmap_local_page(page);
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+				     kaddr, PAGE_SIZE);
+		kunmap_local(kaddr);
+		SSDFS_DBG("\n");
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		magic = (struct ssdfs_signature *)env->log_hdr;
+
+		if (!is_ssdfs_magic_valid(magic))
+			continue;
+
+		if (__is_ssdfs_segment_header_magic_valid(magic)) {
+			seg_hdr = SSDFS_SEG_HDR(env->log_hdr);
+
+			err = ssdfs_check_segment_header(fsi, seg_hdr,
+							 false);
+			if (unlikely(err)) {
+				SSDFS_ERR("log header is corrupted: "
+					  "seg %llu, peb %llu, index %u\n",
+					  pebi->pebc->parent_si->seg_id,
+					  pebi->peb_id,
+					  i);
+				return -EIO;
+			}
+
+			if (*new_log_start_page >= U16_MAX) {
+				SSDFS_ERR("invalid new_log_start_page\n");
+				return -EIO;
+			}
+
+			byte_offset = i * fsi->pagesize;
+			byte_offset += env->log_bytes;
+			byte_offset += fsi->pagesize - 1;
+			page_offset = byte_offset / fsi->pagesize;
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("byte_offset %u, page_offset %u, "
+				  "new_log_start_page %u\n",
+				  byte_offset, page_offset, *new_log_start_page);
+			SSDFS_DBG("log_bytes %u\n", env->log_bytes);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			if (*new_log_start_page < page_offset) {
+#ifdef CONFIG_SSDFS_DEBUG
+				SSDFS_DBG("correct new log start page: "
+					  "old value %u, new value %u\n",
+					  *new_log_start_page,
+					  page_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+				*new_log_start_page = page_offset;
+			} else if (page_offset != *new_log_start_page) {
+				SSDFS_ERR("invalid new log start: "
+					  "page_offset %u, "
+					  "new_log_start_page %u\n",
+					  page_offset,
+					  *new_log_start_page);
+				return -EIO;
+			}
+
+			env->log_offset = (u16)i;
+			pebi->peb_create_time =
+				le64_to_cpu(seg_hdr->peb_create_time);
+			pebi->current_log.last_log_time =
+				le64_to_cpu(seg_hdr->timestamp);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("seg %llu, peb %llu, "
+				  "peb_create_time %llx, last_log_time %llx\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id,
+				  pebi->peb_create_time,
+				  pebi->current_log.last_log_time);
+
+			BUG_ON(pebi->peb_create_time >
+				pebi->current_log.last_log_time);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			goto finish_last_log_search;
+		} else if (is_ssdfs_partial_log_header_magic_valid(magic)) {
+			u32 flags;
+
+			pl_hdr = SSDFS_PLH(env->log_hdr);
+
+			err = ssdfs_check_partial_log_header(fsi, pl_hdr,
+							     false);
+			if (unlikely(err)) {
+				SSDFS_ERR("partial log header is corrupted: "
+					  "seg %llu, peb %llu, index %u\n",
+					  pebi->pebc->parent_si->seg_id,
+					  pebi->peb_id,
+					  i);
+				return -EIO;
+			}
+
+			flags = le32_to_cpu(pl_hdr->pl_flags);
+
+			if (flags & SSDFS_PARTIAL_HEADER_INSTEAD_FOOTER) {
+				/* first partial log */
+#ifdef CONFIG_SSDFS_DEBUG
+				BUG_ON((i + 1) >= U16_MAX);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+				byte_offset = (i + 1) * fsi->pagesize;
+				byte_offset += fsi->pagesize - 1;
+
+#ifdef CONFIG_SSDFS_DEBUG
+				SSDFS_DBG("byte_offset %u, "
+					  "new_log_start_page %u\n",
+					  byte_offset, *new_log_start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+				*new_log_start_page =
+					(u16)(byte_offset / fsi->pagesize);
+				env->log_bytes =
+					le32_to_cpu(pl_hdr->log_bytes);
+
+#ifdef CONFIG_SSDFS_DEBUG
+				SSDFS_DBG("log_bytes %u\n", env->log_bytes);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+				continue;
+			} else if (flags & SSDFS_LOG_HAS_FOOTER) {
+				/* last partial log */
+
+				env->log_bytes =
+					le32_to_cpu(pl_hdr->log_bytes);
+
+				byte_offset = i * fsi->pagesize;
+				byte_offset += env->log_bytes;
+				byte_offset += fsi->pagesize - 1;
+				page_offset = byte_offset / fsi->pagesize;
+
+#ifdef CONFIG_SSDFS_DEBUG
+				SSDFS_DBG("byte_offset %u, page_offset %u, "
+					  "new_log_start_page %u\n",
+					  byte_offset, page_offset, *new_log_start_page);
+				SSDFS_DBG("log_bytes %u\n", env->log_bytes);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+				if (*new_log_start_page < page_offset) {
+#ifdef CONFIG_SSDFS_DEBUG
+					SSDFS_DBG("correct new log start page: "
+						  "old value %u, "
+						  "new value %u\n",
+						  *new_log_start_page,
+						  page_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+					*new_log_start_page = page_offset;
+				} else if (page_offset != *new_log_start_page) {
+					SSDFS_ERR("invalid new log start: "
+						  "page_offset %u, "
+						  "new_log_start_page %u\n",
+						  page_offset,
+						  *new_log_start_page);
+					return -EIO;
+				}
+
+				env->log_offset = (u16)i;
+				pebi->peb_create_time =
+					le64_to_cpu(pl_hdr->peb_create_time);
+				pebi->current_log.last_log_time =
+					le64_to_cpu(pl_hdr->timestamp);
+
+#ifdef CONFIG_SSDFS_DEBUG
+				SSDFS_DBG("seg %llu, peb %llu, "
+					  "peb_create_time %llx, last_log_time %llx\n",
+					  pebi->pebc->parent_si->seg_id,
+					  pebi->peb_id,
+					  pebi->peb_create_time,
+					  pebi->current_log.last_log_time);
+
+				BUG_ON(pebi->peb_create_time >
+					pebi->current_log.last_log_time);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+				goto finish_last_log_search;
+			} else {
+				/* intermediate partial log */
+
+				env->log_bytes =
+					le32_to_cpu(pl_hdr->log_bytes);
+
+				byte_offset = i * fsi->pagesize;
+				byte_offset += env->log_bytes;
+				byte_offset += fsi->pagesize - 1;
+				page_offset = byte_offset / fsi->pagesize;
+
+#ifdef CONFIG_SSDFS_DEBUG
+				SSDFS_DBG("byte_offset %u, page_offset %u, "
+					  "new_log_start_page %u\n",
+					  byte_offset, page_offset, *new_log_start_page);
+				SSDFS_DBG("log_bytes %u\n", env->log_bytes);
+
+				BUG_ON(page_offset >= U16_MAX);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+				*new_log_start_page = (u16)page_offset;
+				env->log_offset = (u16)i;
+				pebi->peb_create_time =
+					le64_to_cpu(pl_hdr->peb_create_time);
+				pebi->current_log.last_log_time =
+					le64_to_cpu(pl_hdr->timestamp);
+
+#ifdef CONFIG_SSDFS_DEBUG
+				SSDFS_DBG("seg %llu, peb %llu, "
+					  "peb_create_time %llx, last_log_time %llx\n",
+					  pebi->pebc->parent_si->seg_id,
+					  pebi->peb_id,
+					  pebi->peb_create_time,
+					  pebi->current_log.last_log_time);
+
+				BUG_ON(pebi->peb_create_time >
+					pebi->current_log.last_log_time);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+				goto finish_last_log_search;
+			}
+		} else if (__is_ssdfs_log_footer_magic_valid(magic)) {
+			footer = SSDFS_LF(env->footer);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			BUG_ON((i + 1) >= U16_MAX);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			byte_offset = (i + 1) * fsi->pagesize;
+			byte_offset += fsi->pagesize - 1;
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("byte_offset %u, new_log_start_page %u\n",
+				  byte_offset, *new_log_start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			*new_log_start_page =
+				(u16)(byte_offset / fsi->pagesize);
+			env->log_bytes =
+				le32_to_cpu(footer->log_bytes);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("log_bytes %u\n", env->log_bytes);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			continue;
+		} else {
+			SSDFS_ERR("log header is corrupted: "
+				  "seg %llu, peb %llu, index %u\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id,
+				  i);
+			return -ERANGE;
+		}
+	}
+
+finish_last_log_search:
+	if (env->log_offset >= fsi->pages_per_peb) {
+		SSDFS_ERR("log_offset %u >= pages_per_peb %u\n",
+			  env->log_offset, fsi->pages_per_peb);
+		return -ERANGE;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	if (fsi->erasesize < env->log_bytes) {
+		SSDFS_WARN("fsi->erasesize %u, log_bytes %u\n",
+			   fsi->erasesize,
+			   env->log_bytes);
+	}
+
+	SSDFS_DBG("seg %llu, peb %llu, peb_index %u, "
+		  "new_log_start_page %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, pebi->peb_index,
+		  *new_log_start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	return 0;
+}
+
+/*
+ * ssdfs_check_log_header() - check log's header
+ * @fsi: file system info object
+ * @env: init environment [in|out]
+ *
+ * This function checks the log's header.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-EIO        - I/O error.
+ * %-ENODATA    - valid magic is not detected.
+ */
+static inline
+int ssdfs_check_log_header(struct ssdfs_fs_info *fsi,
+			   struct ssdfs_read_init_env *env)
+{
+	struct ssdfs_signature *magic = NULL;
+	struct ssdfs_segment_header *seg_hdr = NULL;
+	struct ssdfs_partial_log_header *pl_hdr = NULL;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!env || !env->log_hdr || !env->footer);
+
+	SSDFS_DBG("log_offset %u, log_pages %u\n",
+		  env->log_offset, env->log_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	magic = (struct ssdfs_signature *)env->log_hdr;
+
+	if (!is_ssdfs_magic_valid(magic)) {
+		SSDFS_DBG("valid magic is not detected\n");
+		return -ENODATA;
+	}
+
+	if (__is_ssdfs_segment_header_magic_valid(magic)) {
+		seg_hdr = SSDFS_SEG_HDR(env->log_hdr);
+
+		err = ssdfs_check_segment_header(fsi, seg_hdr,
+						 false);
+		if (unlikely(err)) {
+			SSDFS_ERR("log header is corrupted\n");
+			return -EIO;
+		}
+
+		env->has_seg_hdr = true;
+		env->has_footer = ssdfs_log_has_footer(seg_hdr);
+	} else if (is_ssdfs_partial_log_header_magic_valid(magic)) {
+		pl_hdr = SSDFS_PLH(env->log_hdr);
+
+		err = ssdfs_check_partial_log_header(fsi, pl_hdr,
+						     false);
+		if (unlikely(err)) {
+			SSDFS_ERR("partial log header is corrupted\n");
+			return -EIO;
+		}
+
+		env->has_seg_hdr = false;
+		env->has_footer = ssdfs_pl_has_footer(pl_hdr);
+	} else {
+		SSDFS_DBG("log header is corrupted\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_get_segment_header_blk_bmap_desc() - get block bitmap's descriptor
+ * @pebi: pointer on PEB object
+ * @env: init environment [in]
+ * @desc: block bitmap's descriptor [out]
+ *
+ * This function tries to extract the block bitmap's descriptor.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ */
+static
+int ssdfs_get_segment_header_blk_bmap_desc(struct ssdfs_peb_info *pebi,
+					struct ssdfs_read_init_env *env,
+					struct ssdfs_metadata_descriptor **desc)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_segment_header *seg_hdr = NULL;
+	size_t footer_size = sizeof(struct ssdfs_log_footer);
+	u32 pages_off;
+	u32 bytes_off;
+	struct page *page;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env || !desc);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	*desc = NULL;
+
+	if (!env->has_seg_hdr) {
+		SSDFS_ERR("segment header is absent\n");
+		return -ERANGE;
+	}
+
+	seg_hdr = SSDFS_SEG_HDR(env->log_hdr);
+
+	if (!ssdfs_seg_hdr_has_blk_bmap(seg_hdr)) {
+		if (!env->has_footer) {
+			ssdfs_fs_error(fsi->sb, __FILE__, __func__,
+					__LINE__,
+					"log hasn't footer\n");
+			return -EIO;
+		}
+
+		*desc = &seg_hdr->desc_array[SSDFS_LOG_FOOTER_INDEX];
+
+		bytes_off = le32_to_cpu((*desc)->offset);
+		pages_off = bytes_off / fsi->pagesize;
+
+		page = ssdfs_page_array_get_page_locked(&pebi->cache,
+							pages_off);
+		if (IS_ERR_OR_NULL(page)) {
+			err = ssdfs_read_checked_log_footer(fsi,
+							    env->log_hdr,
+							    pebi->peb_id,
+							    bytes_off,
+							    env->footer,
+							    false);
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to read checked log footer: "
+					  "seg %llu, peb %llu, bytes_off %u\n",
+					  pebi->pebc->parent_si->seg_id,
+					  pebi->peb_id, bytes_off);
+				return err;
+			}
+		} else {
+			ssdfs_memcpy_from_page(env->footer, 0, footer_size,
+						page, 0, PAGE_SIZE,
+						footer_size);
+
+			ssdfs_unlock_page(page);
+			ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+		}
+
+		if (!ssdfs_log_footer_has_blk_bmap(env->footer)) {
+			ssdfs_fs_error(fsi->sb, __FILE__, __func__,
+					__LINE__,
+					"log hasn't block bitmap\n");
+			return -EIO;
+		}
+
+		*desc = &env->footer->desc_array[SSDFS_BLK_BMAP_INDEX];
+	} else
+		*desc = &seg_hdr->desc_array[SSDFS_BLK_BMAP_INDEX];
+
+	return 0;
+}
+
+/*
+ * ssdfs_get_partial_header_blk_bmap_desc() - get block bitmap's descriptor
+ * @pebi: pointer on PEB object
+ * @env: init environment [in]
+ * @desc: block bitmap's descriptor [out]
+ *
+ * This function tries to extract the block bitmap's descriptor.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ */
+static
+int ssdfs_get_partial_header_blk_bmap_desc(struct ssdfs_peb_info *pebi,
+					struct ssdfs_read_init_env *env,
+					struct ssdfs_metadata_descriptor **desc)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_partial_log_header *pl_hdr = NULL;
+	size_t footer_size = sizeof(struct ssdfs_log_footer);
+	u32 pages_off;
+	u32 bytes_off;
+	struct page *page;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env || !desc);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	*desc = NULL;
+
+	if (env->has_seg_hdr) {
+		SSDFS_ERR("partial log header is absent\n");
+		return -ERANGE;
+	}
+
+	pl_hdr = SSDFS_PLH(env->log_hdr);
+
+	if (!ssdfs_pl_hdr_has_blk_bmap(pl_hdr)) {
+		if (!env->has_footer) {
+			ssdfs_fs_error(fsi->sb, __FILE__, __func__,
+					__LINE__,
+					"log hasn't footer\n");
+			return -EIO;
+		}
+
+		*desc = &pl_hdr->desc_array[SSDFS_LOG_FOOTER_INDEX];
+
+		bytes_off = le32_to_cpu((*desc)->offset);
+		pages_off = bytes_off / fsi->pagesize;
+
+		page = ssdfs_page_array_get_page_locked(&pebi->cache,
+							pages_off);
+		if (IS_ERR_OR_NULL(page)) {
+			err = ssdfs_read_checked_log_footer(fsi,
+							    env->log_hdr,
+							    pebi->peb_id,
+							    bytes_off,
+							    env->footer,
+							    false);
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to read checked log footer: "
+					  "seg %llu, peb %llu, bytes_off %u\n",
+					  pebi->pebc->parent_si->seg_id,
+					  pebi->peb_id, bytes_off);
+				return err;
+			}
+		} else {
+			ssdfs_memcpy_from_page(env->footer, 0, footer_size,
+						page, 0, PAGE_SIZE,
+						footer_size);
+
+			ssdfs_unlock_page(page);
+			ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+		}
+
+		if (!ssdfs_log_footer_has_blk_bmap(env->footer)) {
+			ssdfs_fs_error(fsi->sb, __FILE__, __func__,
+					__LINE__,
+					"log hasn't block bitmap\n");
+			return -EIO;
+		}
+
+		*desc = &env->footer->desc_array[SSDFS_BLK_BMAP_INDEX];
+	} else
+		*desc = &pl_hdr->desc_array[SSDFS_BLK_BMAP_INDEX];
+
+	return 0;
+}
+
+/*
+ * ssdfs_pre_fetch_block_bitmap() - pre-fetch block bitmap
+ * @pebi: pointer on PEB object
+ * @env: init environment [in|out]
+ *
+ * This function tries to check the presence of block bitmap
+ * in the PEB's cache. Otherwise, it tries to read the block
+ * bitmap from the volume into the PEB's cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ * %-ENOMEM     - fail to allocate memory.
+ */
+static
+int ssdfs_pre_fetch_block_bitmap(struct ssdfs_peb_info *pebi,
+				 struct ssdfs_read_init_env *env)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_metadata_descriptor *desc = NULL;
+	struct page *page;
+	void *kaddr;
+	u32 pages_off;
+	u32 bytes_off;
+	size_t hdr_buf_size = sizeof(struct ssdfs_segment_header);
+	u32 area_offset, area_size;
+	u32 cur_page, page_start, page_end;
+	size_t read_bytes;
+	size_t bmap_hdr_size = sizeof(struct ssdfs_block_bitmap_header);
+	u32 pebsize;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env);
+
+	SSDFS_DBG("seg %llu, peb %llu, peb_index %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, pebi->peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	pages_off = env->log_offset;
+	pebsize = fsi->pages_per_peb * fsi->pagesize;
+
+	page = ssdfs_page_array_get_page_locked(&pebi->cache, pages_off);
+	if (IS_ERR_OR_NULL(page)) {
+		err = ssdfs_read_checked_segment_header(fsi,
+							pebi->peb_id,
+							pages_off,
+							env->log_hdr,
+							false);
+		if (err) {
+			SSDFS_ERR("fail to read checked segment header: "
+				  "peb %llu, err %d\n",
+				  pebi->peb_id, err);
+			return err;
+		}
+	} else {
+		ssdfs_memcpy_from_page(env->log_hdr, 0, hdr_buf_size,
+					page, 0, PAGE_SIZE,
+					hdr_buf_size);
+
+		ssdfs_unlock_page(page);
+		ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("page %p, count %d\n",
+			  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+	}
+
+	err = ssdfs_check_log_header(fsi, env);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to check log header: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	if (env->has_seg_hdr)
+		err = ssdfs_get_segment_header_blk_bmap_desc(pebi, env, &desc);
+	else
+		err = ssdfs_get_partial_header_blk_bmap_desc(pebi, env, &desc);
+
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to get descriptor: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	if (!desc) {
+		SSDFS_ERR("invalid descriptor pointer\n");
+		return -ERANGE;
+	}
+
+	area_offset = le32_to_cpu(desc->offset);
+	area_size = le32_to_cpu(desc->size);
+
+	if (bmap_hdr_size != le16_to_cpu(desc->check.bytes)) {
+		ssdfs_fs_error(fsi->sb, __FILE__, __func__, __LINE__,
+				"bmap_hdr_size %zu != desc->check.bytes %u\n",
+				bmap_hdr_size,
+				le16_to_cpu(desc->check.bytes));
+		return -EIO;
+	}
+
+	if (area_offset >= pebsize) {
+		ssdfs_fs_error(fsi->sb, __FILE__, __func__, __LINE__,
+				"desc->offset %u >= pebsize %u\n",
+				area_offset, pebsize);
+		return -EIO;
+	}
+
+	bytes_off = area_offset;
+	page_start = bytes_off / fsi->pagesize;
+	bytes_off += area_size - 1;
+	page_end = bytes_off / fsi->pagesize;
+
+	for (cur_page = page_start; cur_page <= page_end; cur_page++) {
+		page = ssdfs_page_array_get_page_locked(&pebi->cache,
+							cur_page);
+		if (IS_ERR_OR_NULL(page)) {
+			page = ssdfs_page_array_grab_page(&pebi->cache,
+							  cur_page);
+			if (unlikely(IS_ERR_OR_NULL(page))) {
+				SSDFS_ERR("fail to grab page: index %u\n",
+					  cur_page);
+				return -ENOMEM;
+			}
+
+			kaddr = kmap_local_page(page);
+			err = ssdfs_aligned_read_buffer(fsi, pebi->peb_id,
+							cur_page * PAGE_SIZE,
+							(u8 *)kaddr,
+							PAGE_SIZE,
+							&read_bytes);
+			kunmap_local(kaddr);
+
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to read memory page: "
+					  "index %u, err %d\n",
+					  cur_page, err);
+				goto finish_read_page;
+			} else if (unlikely(read_bytes != PAGE_SIZE)) {
+				err = -ERANGE;
+				SSDFS_ERR("invalid read_bytes %zu\n",
+					  read_bytes);
+				goto finish_read_page;
+			}
+
+			SetPageUptodate(page);
+
+finish_read_page:
+			ssdfs_unlock_page(page);
+			ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+		} else {
+			ssdfs_unlock_page(page);
+			ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+		}
+	}
+
+	return err;
+}
+
+/*
+ * ssdfs_get_segment_header_blk2off_tbl_desc() - get blk2off tbl's descriptor
+ * @pebi: pointer on PEB object
+ * @env: init environment [in]
+ * @desc: blk2off tbl's descriptor [out]
+ *
+ * This function tries to extract the blk2off table's descriptor.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ * %-ENOENT     - blk2off table's descriptor is absent.
+ */
+static inline
+int ssdfs_get_segment_header_blk2off_tbl_desc(struct ssdfs_peb_info *pebi,
+					struct ssdfs_read_init_env *env,
+					struct ssdfs_metadata_descriptor **desc)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_segment_header *seg_hdr = NULL;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env || !desc);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*desc = NULL;
+	fsi = pebi->pebc->parent_si->fsi;
+
+	if (!env->has_seg_hdr) {
+		SSDFS_ERR("segment header is absent\n");
+		return -ERANGE;
+	}
+
+	seg_hdr = SSDFS_SEG_HDR(env->log_hdr);
+
+	if (!ssdfs_seg_hdr_has_offset_table(seg_hdr)) {
+		if (!env->has_footer) {
+			ssdfs_fs_error(fsi->sb, __FILE__,
+					__func__, __LINE__,
+					"log hasn't footer\n");
+			return -EIO;
+		}
+
+		if (!ssdfs_log_footer_has_offset_table(env->footer)) {
+			SSDFS_DBG("log hasn't blk2off table\n");
+			return -ENOENT;
+		}
+
+		*desc = &env->footer->desc_array[SSDFS_OFF_TABLE_INDEX];
+	} else
+		*desc = &seg_hdr->desc_array[SSDFS_OFF_TABLE_INDEX];
+
+	return 0;
+}
+
+/*
+ * ssdfs_get_segment_header_blk_desc_tbl_desc() - get blk desc tbl's descriptor
+ * @pebi: pointer on PEB object
+ * @env: init environment [in]
+ * @desc: blk desc tbl's descriptor [out]
+ *
+ * This function tries to extract the block descriptor table's descriptor.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ * %-ENOENT     - block descriptor table's descriptor is absent.
+ */
+static inline
+int ssdfs_get_segment_header_blk_desc_tbl_desc(struct ssdfs_peb_info *pebi,
+					struct ssdfs_read_init_env *env,
+					struct ssdfs_metadata_descriptor **desc)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_segment_header *seg_hdr = NULL;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env || !desc);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*desc = NULL;
+	fsi = pebi->pebc->parent_si->fsi;
+
+	if (!env->has_seg_hdr) {
+		SSDFS_ERR("segment header is absent\n");
+		return -ERANGE;
+	}
+
+	seg_hdr = SSDFS_SEG_HDR(env->log_hdr);
+
+	if (!ssdfs_log_has_blk_desc_chain(seg_hdr)) {
+		SSDFS_DBG("log hasn't block descriptor table\n");
+		return -ENOENT;
+	} else
+		*desc = &seg_hdr->desc_array[SSDFS_BLK_DESC_AREA_INDEX];
+
+	return 0;
+}
+
+/*
+ * ssdfs_get_partial_header_blk2off_tbl_desc() - get blk2off tbl's descriptor
+ * @pebi: pointer on PEB object
+ * @env: init environment [in]
+ * @desc: blk2off tbl's descriptor [out]
+ *
+ * This function tries to extract the blk2off table's descriptor.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ * %-ENOENT     - blk2off table's descriptor is absent.
+ */
+static inline
+int ssdfs_get_partial_header_blk2off_tbl_desc(struct ssdfs_peb_info *pebi,
+					struct ssdfs_read_init_env *env,
+					struct ssdfs_metadata_descriptor **desc)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_partial_log_header *pl_hdr = NULL;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env || !desc);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*desc = NULL;
+	fsi = pebi->pebc->parent_si->fsi;
+
+	if (env->has_seg_hdr) {
+		SSDFS_ERR("partial log header is absent\n");
+		return -ERANGE;
+	}
+
+	pl_hdr = SSDFS_PLH(env->log_hdr);
+
+	if (!ssdfs_pl_hdr_has_offset_table(pl_hdr)) {
+		if (!env->has_footer) {
+			SSDFS_DBG("log hasn't blk2off table\n");
+			return -ENOENT;
+		}
+
+		if (!ssdfs_log_footer_has_offset_table(env->footer)) {
+			SSDFS_DBG("log hasn't blk2off table\n");
+			return -ENOENT;
+		}
+
+		*desc = &env->footer->desc_array[SSDFS_OFF_TABLE_INDEX];
+	} else
+		*desc = &pl_hdr->desc_array[SSDFS_OFF_TABLE_INDEX];
+
+	return 0;
+}
+
+/*
+ * ssdfs_get_partial_header_blk_desc_tbl_desc() - get blk desc tbl's descriptor
+ * @pebi: pointer on PEB object
+ * @env: init environment [in]
+ * @desc: blk desc tbl's descriptor [out]
+ *
+ * This function tries to extract the block descriptor table's descriptor.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ * %-ENOENT     - block descriptor table's descriptor is absent.
+ */
+static inline
+int ssdfs_get_partial_header_blk_desc_tbl_desc(struct ssdfs_peb_info *pebi,
+					struct ssdfs_read_init_env *env,
+					struct ssdfs_metadata_descriptor **desc)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_partial_log_header *pl_hdr = NULL;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env || !desc);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*desc = NULL;
+	fsi = pebi->pebc->parent_si->fsi;
+
+	if (env->has_seg_hdr) {
+		SSDFS_ERR("partial log header is absent\n");
+		return -ERANGE;
+	}
+
+	pl_hdr = SSDFS_PLH(env->log_hdr);
+
+	if (!ssdfs_pl_has_blk_desc_chain(pl_hdr)) {
+		SSDFS_DBG("log hasn't block descriptor table\n");
+		return -ENOENT;
+	} else
+		*desc = &pl_hdr->desc_array[SSDFS_BLK_DESC_AREA_INDEX];
+
+	return 0;
+}
+
+/*
+ * ssdfs_pre_fetch_metadata_area() - pre-fetch metadata area
+ * @pebi: pointer on PEB object
+ * @desc: metadata area's descriptor
+ *
+ * This function tries to check the presence of metadata area
+ * in the PEB's cache. Otherwise, it tries to read the metadata area
+ * from the volume into the PEB's cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ * %-ENOMEM     - fail to allocate memory.
+ */
+static
+int ssdfs_pre_fetch_metadata_area(struct ssdfs_peb_info *pebi,
+				  struct ssdfs_metadata_descriptor *desc)
+{
+	struct ssdfs_fs_info *fsi;
+	struct page *page;
+	void *kaddr;
+	u32 bytes_off;
+	u32 area_offset, area_size;
+	u32 cur_page, page_start, page_end;
+	size_t read_bytes;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !desc);
+
+	SSDFS_DBG("seg %llu, peb %llu, peb_index %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, pebi->peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	area_offset = le32_to_cpu(desc->offset);
+	area_size = le32_to_cpu(desc->size);
+
+	bytes_off = area_offset;
+	page_start = bytes_off / fsi->pagesize;
+	bytes_off += area_size - 1;
+	page_end = bytes_off / fsi->pagesize;
+
+	for (cur_page = page_start; cur_page <= page_end; cur_page++) {
+		page = ssdfs_page_array_get_page_locked(&pebi->cache,
+							cur_page);
+		if (IS_ERR_OR_NULL(page)) {
+			page = ssdfs_page_array_grab_page(&pebi->cache,
+							  cur_page);
+			if (unlikely(IS_ERR_OR_NULL(page))) {
+				SSDFS_ERR("fail to grab page: index %u\n",
+					  cur_page);
+				return -ENOMEM;
+			}
+
+			kaddr = kmap_local_page(page);
+			err = ssdfs_aligned_read_buffer(fsi, pebi->peb_id,
+							cur_page * PAGE_SIZE,
+							(u8 *)kaddr,
+							PAGE_SIZE,
+							&read_bytes);
+			flush_dcache_page(page);
+			kunmap_local(kaddr);
+
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to read memory page: "
+					  "index %u, err %d\n",
+					  cur_page, err);
+				goto finish_read_page;
+			} else if (unlikely(read_bytes != PAGE_SIZE)) {
+				err = -ERANGE;
+				SSDFS_ERR("invalid read_bytes %zu\n",
+					  read_bytes);
+				goto finish_read_page;
+			}
+
+			SetPageUptodate(page);
+
+finish_read_page:
+			ssdfs_unlock_page(page);
+			ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+		} else {
+			ssdfs_unlock_page(page);
+			ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+		}
+	}
+
+	return err;
+}
+
+/*
+ * ssdfs_pre_fetch_blk2off_table_area() - pre-fetch blk2off table
+ * @pebi: pointer on PEB object
+ * @env: init environment [in|out]
+ *
+ * This function tries to check the presence of blk2off table
+ * in the PEB's cache. Otherwise, it tries to read the blk2off table
+ * from the volume into the PEB's cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ * %-ENOMEM     - fail to allocate memory.
+ * %-ENOENT     - blk2off table is absent.
+ */
+static
+int ssdfs_pre_fetch_blk2off_table_area(struct ssdfs_peb_info *pebi,
+					struct ssdfs_read_init_env *env)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_metadata_descriptor *desc = NULL;
+	struct page *page;
+	u32 pages_off;
+	size_t hdr_buf_size = sizeof(struct ssdfs_segment_header);
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env);
+
+	SSDFS_DBG("seg %llu, peb %llu, peb_index %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, pebi->peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	pages_off = env->log_offset;
+
+	page = ssdfs_page_array_get_page_locked(&pebi->cache, pages_off);
+	if (IS_ERR_OR_NULL(page)) {
+		err = ssdfs_read_checked_segment_header(fsi,
+							pebi->peb_id,
+							pages_off,
+							env->log_hdr,
+							false);
+		if (err) {
+			SSDFS_ERR("fail to read checked segment header: "
+				  "peb %llu, err %d\n",
+				  pebi->peb_id, err);
+			return err;
+		}
+	} else {
+		ssdfs_memcpy_from_page(env->log_hdr, 0, hdr_buf_size,
+					page, 0, PAGE_SIZE,
+					hdr_buf_size);
+
+		ssdfs_unlock_page(page);
+		ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("page %p, count %d\n",
+			  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+	}
+
+	err = ssdfs_check_log_header(fsi, env);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to check log header: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	if (env->has_seg_hdr) {
+		err = ssdfs_get_segment_header_blk2off_tbl_desc(pebi, env,
+								&desc);
+	} else {
+		err = ssdfs_get_partial_header_blk2off_tbl_desc(pebi, env,
+								&desc);
+	}
+
+	if (err == -ENOENT) {
+		SSDFS_DBG("blk2off table is absent\n");
+		return err;
+	} else if (unlikely(err)) {
+		SSDFS_ERR("fail to get descriptor: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	if (!desc) {
+		SSDFS_ERR("invalid descriptor pointer\n");
+		return -ERANGE;
+	}
+
+	err = ssdfs_pre_fetch_metadata_area(pebi, desc);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to pre-fetch a metadata area: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_read_blk_desc_byte_stream() - read blk desc's byte stream
+ * @pebi: pointer on PEB object
+ * @read_bytes: amount of bytes for reading
+ * @env: init environment [in|out]
+ *
+ * This function tries to read blk desc table's byte stream.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-ENOMEM     - fail to allocate memory.
+ * %-EIO        - I/O error.
+ */
+static
+int ssdfs_read_blk_desc_byte_stream(struct ssdfs_peb_info *pebi,
+				    u32 read_bytes,
+				    struct ssdfs_read_init_env *env)
+{
+	struct ssdfs_fs_info *fsi;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !pebi->pebc->parent_si);
+	BUG_ON(!pebi->pebc->parent_si->fsi);
+	BUG_ON(!env);
+
+	SSDFS_DBG("seg %llu, peb %llu, read_bytes %u, "
+		  "read_off %u, write_off %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  read_bytes, env->bdt_init.read_off,
+		  env->bdt_init.write_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	while (read_bytes > 0) {
+		struct page *page = NULL;
+		void *kaddr;
+		pgoff_t page_index = env->bdt_init.write_off >> PAGE_SHIFT;
+		u32 capacity = pagevec_count(&env->bdt_init.pvec) << PAGE_SHIFT;
+		u32 offset, bytes;
+
+		if (env->bdt_init.write_off >= capacity) {
+			if (pagevec_space(&env->bdt_init.pvec) == 0) {
+				/*
+				 * Block descriptor table byte stream could be
+				 * bigger than page vector capacity.
+				 * As a result, not complete byte stream will
+				 * read and initialization will be done only
+				 * partially. The rest byte stream will be
+				 * extracted and be used for initialization
+				 * for request of particular logical block.
+				 */
+				SSDFS_DBG("pagevec is full\n");
+				return 0;
+			}
+
+			page = ssdfs_read_add_pagevec_page(&env->bdt_init.pvec);
+			if (unlikely(IS_ERR_OR_NULL(page))) {
+				err = !page ? -ENOMEM : PTR_ERR(page);
+				SSDFS_ERR("fail to add pagevec page: err %d\n",
+					  err);
+				return err;
+			}
+		} else {
+			page = env->bdt_init.pvec.pages[page_index];
+			if (unlikely(!page)) {
+				err = -ERANGE;
+				SSDFS_ERR("fail to get page: err %d\n",
+					  err);
+				return err;
+			}
+		}
+
+		offset = env->bdt_init.write_off % PAGE_SIZE;
+		bytes = min_t(u32, read_bytes, PAGE_SIZE - offset);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("offset %u, bytes %u\n",
+			  offset, bytes);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		ssdfs_lock_page(page);
+		kaddr = kmap_local_page(page);
+		err = ssdfs_unaligned_read_cache(pebi,
+						 env->bdt_init.read_off, bytes,
+						 (u8 *)kaddr + offset);
+		flush_dcache_page(page);
+		kunmap_local(kaddr);
+		ssdfs_unlock_page(page);
+
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to read page: "
+				  "seg %llu, peb %llu, offset %u, "
+				  "size %u, err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id, env->bdt_init.read_off,
+				  bytes, err);
+			return err;
+		}
+
+		read_bytes -= bytes;
+		env->bdt_init.read_off += bytes;
+		env->bdt_init.write_off += bytes;
+	};
+
+	return 0;
+}
+
+/*
+ * ssdfs_read_blk_desc_compressed_byte_stream() - read blk desc's byte stream
+ * @pebi: pointer on PEB object
+ * @read_bytes: amount of bytes for reading
+ * @env: init environment [in|out]
+ *
+ * This function tries to read blk desc table's byte stream.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-ENOMEM     - fail to allocate memory.
+ * %-EIO        - I/O error.
+ */
+static
+int ssdfs_read_blk_desc_compressed_byte_stream(struct ssdfs_peb_info *pebi,
+						u32 read_bytes,
+						struct ssdfs_read_init_env *env)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_area_block_table table;
+	struct ssdfs_fragment_desc *frag;
+	struct page *page = NULL;
+	void *kaddr;
+	size_t tbl_size = sizeof(struct ssdfs_area_block_table);
+	u32 area_offset;
+	u16 fragments_count;
+	u16 frag_uncompr_size;
+	int i;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !pebi->pebc->parent_si);
+	BUG_ON(!pebi->pebc->parent_si->fsi);
+	BUG_ON(!env);
+
+	SSDFS_DBG("seg %llu, peb %llu, read_bytes %u, "
+		  "read_off %u, write_off %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  read_bytes, env->bdt_init.read_off,
+		  env->bdt_init.write_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	area_offset = env->bdt_init.read_off;
+
+	err = ssdfs_unaligned_read_cache(pebi, area_offset, tbl_size, &table);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to read area block table: "
+			  "area_offset %u, tbl_size %zu, err %d\n",
+			  area_offset, tbl_size, err);
+		return err;
+	}
+
+	if (table.chain_hdr.magic != SSDFS_CHAIN_HDR_MAGIC) {
+		SSDFS_ERR("corrupted area block table: "
+			  "magic (expected %#x, found %#x)\n",
+			  SSDFS_CHAIN_HDR_MAGIC,
+			  table.chain_hdr.magic);
+		return -EIO;
+	}
+
+	switch (table.chain_hdr.type) {
+	case SSDFS_BLK_DESC_ZLIB_CHAIN_HDR:
+	case SSDFS_BLK_DESC_LZO_CHAIN_HDR:
+		/* expected type */
+		break;
+
+	default:
+		SSDFS_ERR("unexpected area block table's type %#x\n",
+			  table.chain_hdr.type);
+		return -EIO;
+	}
+
+	fragments_count = le16_to_cpu(table.chain_hdr.fragments_count);
+
+	for (i = 0; i < fragments_count; i++) {
+		frag = &table.blk[i];
+
+		if (frag->magic != SSDFS_FRAGMENT_DESC_MAGIC) {
+			SSDFS_ERR("corrupted area block table: "
+				  "magic (expected %#x, found %#x)\n",
+				  SSDFS_FRAGMENT_DESC_MAGIC,
+				  frag->magic);
+			return -EIO;
+		}
+
+		switch (frag->type) {
+		case SSDFS_DATA_BLK_DESC_ZLIB:
+		case SSDFS_DATA_BLK_DESC_LZO:
+			/* expected type */
+			break;
+
+		default:
+			SSDFS_ERR("unexpected fragment's type %#x\n",
+				  frag->type);
+			return -EIO;
+		}
+
+		frag_uncompr_size = le16_to_cpu(frag->uncompr_size);
+
+		page = ssdfs_read_add_pagevec_page(&env->bdt_init.pvec);
+		if (unlikely(IS_ERR_OR_NULL(page))) {
+			err = !page ? -ENOMEM : PTR_ERR(page);
+			SSDFS_ERR("fail to add pagevec page: err %d\n",
+				  err);
+			return err;
+		}
+
+		ssdfs_lock_page(page);
+		kaddr = kmap_local_page(page);
+		err = __ssdfs_decompress_blk_desc_fragment(pebi, frag,
+							   area_offset,
+							   kaddr, PAGE_SIZE);
+		flush_dcache_page(page);
+		kunmap_local(kaddr);
+		ssdfs_unlock_page(page);
+
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to read page: "
+				  "seg %llu, peb %llu, offset %u, "
+				  "size %u, err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id, env->bdt_init.read_off,
+				  frag_uncompr_size, err);
+			return err;
+		}
+
+		env->bdt_init.read_off += frag_uncompr_size;
+		env->bdt_init.write_off += frag_uncompr_size;
+	}
+
+	return err;
+}
+
+/*
+ * ssdfs_pre_fetch_blk_desc_table_area() - pre-fetch blk desc table
+ * @pebi: pointer on PEB object
+ * @env: init environment [in|out]
+ *
+ * This function tries to check the presence of blk desc table
+ * in the PEB's cache. Otherwise, it tries to read the blk desc table
+ * from the volume into the PEB's cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ * %-EIO        - I/O error.
+ * %-ENOMEM     - fail to allocate memory.
+ * %-ENOENT     - blk desc table is absent.
+ */
+static
+int ssdfs_pre_fetch_blk_desc_table_area(struct ssdfs_peb_info *pebi,
+					struct ssdfs_read_init_env *env)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_metadata_descriptor *desc = NULL;
+	struct page *page;
+	u32 pages_off;
+	size_t hdr_buf_size = sizeof(struct ssdfs_segment_header);
+	u16 flags;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !env);
+
+	SSDFS_DBG("seg %llu, peb %llu, peb_index %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, pebi->peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	pages_off = env->log_offset;
+	env->bdt_init.read_off = 0;
+	env->bdt_init.write_off = 0;
+
+	page = ssdfs_page_array_get_page_locked(&pebi->cache, pages_off);
+	if (IS_ERR_OR_NULL(page)) {
+		err = ssdfs_read_checked_segment_header(fsi,
+							pebi->peb_id,
+							pages_off,
+							env->log_hdr,
+							false);
+		if (err) {
+			SSDFS_ERR("fail to read checked segment header: "
+				  "peb %llu, err %d\n",
+				  pebi->peb_id, err);
+			return err;
+		}
+	} else {
+		ssdfs_memcpy_from_page(env->log_hdr, 0, hdr_buf_size,
+					page, 0, PAGE_SIZE,
+					hdr_buf_size);
+
+		ssdfs_unlock_page(page);
+		ssdfs_put_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("page %p, count %d\n",
+			  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+	}
+
+	err = ssdfs_check_log_header(fsi, env);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to check log header: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	if (env->has_seg_hdr) {
+		err = ssdfs_get_segment_header_blk_desc_tbl_desc(pebi, env,
+								 &desc);
+	} else {
+		err = ssdfs_get_partial_header_blk_desc_tbl_desc(pebi, env,
+								 &desc);
+	}
+
+	if (err == -ENOENT) {
+		SSDFS_DBG("blk descriptor table is absent\n");
+		return err;
+	} else if (unlikely(err)) {
+		SSDFS_ERR("fail to get descriptor: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	if (!desc) {
+		SSDFS_ERR("invalid descriptor pointer\n");
+		return -ERANGE;
+	}
+
+	env->bdt_init.read_off = le32_to_cpu(desc->offset);
+
+	err = ssdfs_pre_fetch_metadata_area(pebi, desc);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to pre-fetch a metadata area: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	flags = le16_to_cpu(desc->check.flags);
+
+	if ((flags & SSDFS_ZLIB_COMPRESSED) && (flags & SSDFS_LZO_COMPRESSED)) {
+		SSDFS_ERR("invalid set of flags: "
+			  "flags %#x\n",
+			  flags);
+		return -ERANGE;
+	}
+
+	if ((flags & SSDFS_ZLIB_COMPRESSED) || (flags & SSDFS_LZO_COMPRESSED)) {
+		err = ssdfs_read_blk_desc_compressed_byte_stream(pebi,
+						      le32_to_cpu(desc->size),
+						      env);
+	} else {
+		u32 read_bytes = le32_to_cpu(desc->size);
+		size_t area_tbl_size = sizeof(struct ssdfs_area_block_table);
+
+		env->bdt_init.read_off += area_tbl_size;
+
+		if (read_bytes <= area_tbl_size) {
+			SSDFS_ERR("corrupted area blocks table: "
+				  "read_bytes %u, area_tbl_size %zu\n",
+				  read_bytes, area_tbl_size);
+			return -EIO;
+		}
+
+		read_bytes -= area_tbl_size;
+
+		err = ssdfs_read_blk_desc_byte_stream(pebi, read_bytes, env);
+	}
+
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to prepare block descriptor table: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
 /*
  * ssdfs_read_checked_block_bitmap_header() - read and check block bitmap header
  * @pebi: pointer on PEB object
