diff --git a/fs/ssdfs/peb_flush_thread.c b/fs/ssdfs/peb_flush_thread.c
index d9352804f6b9..2de4bb806678 100644
--- a/fs/ssdfs/peb_flush_thread.c
+++ b/fs/ssdfs/peb_flush_thread.c
@@ -113,6 +113,3347 @@ void ssdfs_flush_check_memory_leaks(void)
  *                         FLUSH THREAD FUNCTIONALITY                         *
  ******************************************************************************/
 
+/*
+ * ssdfs_peb_has_dirty_pages() - check that PEB has dirty pages
+ * @pebi: pointer on PEB object
+ */
+bool ssdfs_peb_has_dirty_pages(struct ssdfs_peb_info *pebi)
+{
+	struct ssdfs_page_array *area_pages;
+	bool is_peb_dirty = false;
+	int i;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	for (i = 0; i < SSDFS_LOG_AREA_MAX; i++) {
+		area_pages = &pebi->current_log.area[i].array;
+
+		if (atomic_read(&area_pages->state) == SSDFS_PAGE_ARRAY_DIRTY) {
+			is_peb_dirty = true;
+			break;
+		}
+	}
+
+	return is_peb_dirty;
+}
+
+/*
+ * is_full_log_ready() - check that full log is ready
+ * @pebi: pointer on PEB object
+ */
+static inline
+bool is_full_log_ready(struct ssdfs_peb_info *pebi)
+{
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, free_data_pages %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id,
+		  pebi->current_log.free_data_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	return pebi->current_log.free_data_pages == 0;
+}
+
+/*
+ * should_partial_log_being_commited() - check that it's time to commit
+ * @pebi: pointer on PEB object
+ */
+static inline
+bool should_partial_log_being_commited(struct ssdfs_peb_info *pebi)
+{
+	u16 free_data_pages;
+	u16 min_partial_log_pages;
+	int log_strategy;
+	bool time_to_commit = false;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	free_data_pages = pebi->current_log.free_data_pages;
+	min_partial_log_pages = ssdfs_peb_estimate_min_partial_log_pages(pebi);
+
+	log_strategy = is_log_partial(pebi);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("seg %llu, peb %llu, log_strategy %#x, "
+		  "free_data_pages %u, min_partial_log_pages %u\n",
+		  pebi->pebc->parent_si->seg_id,
+		  pebi->peb_id, log_strategy,
+		  free_data_pages, min_partial_log_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	switch (log_strategy) {
+	case SSDFS_START_FULL_LOG:
+	case SSDFS_START_PARTIAL_LOG:
+		if (free_data_pages <= min_partial_log_pages) {
+			time_to_commit = true;
+		} else {
+			time_to_commit = false;
+		}
+		break;
+
+	case SSDFS_CONTINUE_PARTIAL_LOG:
+	case SSDFS_FINISH_PARTIAL_LOG:
+	case SSDFS_FINISH_FULL_LOG:
+		/* do nothing */
+		time_to_commit = false;
+		break;
+
+	default:
+		SSDFS_CRIT("unexpected log strategy %#x\n",
+			   log_strategy);
+		time_to_commit = false;
+		break;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("time_to_commit %#x\n", time_to_commit);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	return time_to_commit;
+}
+
+/*
+ * ssdfs_reserve_segment_header() - reserve space for segment header
+ * @pebi: pointer on PEB object
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function reserves space for segment header in PEB's cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-EINVAL     - invalid input.
+ * %-ENOMEM     - fail to allocate page.
+ */
+static
+int ssdfs_reserve_segment_header(struct ssdfs_peb_info *pebi,
+				 pgoff_t *cur_page, u32 *write_offset)
+{
+	struct page *page;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!cur_page || !write_offset);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  *cur_page, *write_offset);
+
+	if (*cur_page != pebi->current_log.start_page) {
+		SSDFS_ERR("cur_page %lu != start_page %u\n",
+			  *cur_page, pebi->current_log.start_page);
+		return -EINVAL;
+	}
+
+	if (*write_offset != 0) {
+		SSDFS_ERR("write_offset %u != 0\n",
+			  *write_offset);
+		return -EINVAL;
+	}
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	page = ssdfs_page_array_grab_page(&pebi->cache, *cur_page);
+	if (IS_ERR_OR_NULL(page)) {
+		SSDFS_ERR("fail to grab cache page: index %lu\n",
+			  *cur_page);
+		return -ENOMEM;
+	}
+
+	/* prepare header space */
+	ssdfs_memset_page(page, 0, PAGE_SIZE, 0xFF, PAGE_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 */
+
+	*write_offset = offsetof(struct ssdfs_segment_header, payload);
+
+	return 0;
+}
+
+/*
+ * ssdfs_reserve_partial_log_header() - reserve space for partial log's header
+ * @pebi: pointer on PEB object
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function reserves space for partial log's header in PEB's cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-EINVAL     - invalid input.
+ * %-ENOMEM     - fail to allocate page.
+ */
+static
+int ssdfs_reserve_partial_log_header(struct ssdfs_peb_info *pebi,
+				     pgoff_t *cur_page, u32 *write_offset)
+{
+	struct page *page;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!cur_page || !write_offset);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  *cur_page, *write_offset);
+
+	if (*cur_page != pebi->current_log.start_page) {
+		SSDFS_ERR("cur_page %lu != start_page %u\n",
+			  *cur_page, pebi->current_log.start_page);
+		return -EINVAL;
+	}
+
+	if (*write_offset != 0) {
+		SSDFS_ERR("write_offset %u != 0\n",
+			  *write_offset);
+		return -EINVAL;
+	}
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	page = ssdfs_page_array_grab_page(&pebi->cache, *cur_page);
+	if (IS_ERR_OR_NULL(page)) {
+		SSDFS_ERR("fail to grab cache page: index %lu\n",
+			  *cur_page);
+		return -ENOMEM;
+	}
+
+	/* prepare header space */
+	ssdfs_memset_page(page, 0, PAGE_SIZE, 0xFF, PAGE_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 */
+
+	*write_offset = offsetof(struct ssdfs_partial_log_header, payload);
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_store_pagevec() - store pagevec into page cache
+ * @desc: descriptor of pagevec environment
+ *
+ * This function tries to store pagevec into log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_pagevec(struct ssdfs_pagevec_descriptor *desc)
+{
+	struct ssdfs_fs_info *fsi;
+	struct page *src_page, *dst_page;
+	unsigned char *kaddr;
+	int i;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!desc);
+	BUG_ON(!desc->pebi || !desc->pebi->pebc->parent_si);
+	BUG_ON(!desc->pebi->pebc->parent_si->fsi);
+	BUG_ON(!desc->page_vec || !desc->desc_array);
+	BUG_ON(!desc->cur_page || !desc->write_offset);
+
+	switch (desc->compression_type) {
+	case SSDFS_FRAGMENT_UNCOMPR_BLOB:
+	case SSDFS_FRAGMENT_ZLIB_BLOB:
+	case SSDFS_FRAGMENT_LZO_BLOB:
+		/* valid type */
+		break;
+
+	default:
+		SSDFS_WARN("invalid compression %#x\n",
+			   desc->compression_type);
+		return -EINVAL;
+	}
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u\n",
+		  desc->pebi->pebc->parent_si->seg_id,
+		  desc->pebi->peb_id,
+		  desc->pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = desc->pebi->pebc->parent_si->fsi;
+	desc->compr_size = 0;
+	desc->uncompr_size = 0;
+	desc->fragments_count = 0;
+
+	for (i = 0; i < ssdfs_page_vector_count(desc->page_vec); i++) {
+		size_t iter_bytes;
+		size_t dst_page_off;
+		size_t dst_free_space;
+		struct ssdfs_fragment_source from;
+		struct ssdfs_fragment_destination to;
+
+		BUG_ON(i >= desc->array_capacity);
+
+		if (desc->uncompr_size > desc->bytes_count) {
+			SSDFS_WARN("uncompr_size %u > bytes_count %zu\n",
+				   desc->uncompr_size,
+				   desc->bytes_count);
+			break;
+		} else if (desc->uncompr_size == desc->bytes_count)
+			break;
+
+		iter_bytes = min_t(size_t, PAGE_SIZE,
+				   desc->bytes_count - desc->uncompr_size);
+
+		src_page = desc->page_vec->pages[i];
+
+try_get_next_page:
+		dst_page = ssdfs_page_array_grab_page(&desc->pebi->cache,
+						      *desc->cur_page);
+		if (IS_ERR_OR_NULL(dst_page)) {
+			SSDFS_ERR("fail to grab cache page: index %lu\n",
+				  *desc->cur_page);
+			return -ENOMEM;
+		}
+
+		dst_page_off = *(desc->write_offset) % PAGE_SIZE;
+		dst_free_space = PAGE_SIZE - dst_page_off;
+
+		kaddr = kmap_local_page(dst_page);
+
+		from.page = src_page;
+		from.start_offset = 0;
+		from.data_bytes = iter_bytes;
+		from.sequence_id = desc->start_sequence_id + i;
+		from.fragment_type = desc->compression_type;
+		from.fragment_flags = SSDFS_FRAGMENT_HAS_CSUM;
+
+		to.area_offset = desc->area_offset;
+		to.write_offset = *desc->write_offset;
+		to.store = kaddr + dst_page_off;
+		to.free_space = dst_free_space;
+		to.compr_size = 0;
+		to.desc = &desc->desc_array[i];
+
+		err = ssdfs_peb_store_fragment(&from, &to);
+
+		flush_dcache_page(dst_page);
+		kunmap_local(kaddr);
+
+		if (!err) {
+			ssdfs_set_page_private(dst_page, 0);
+			SetPageUptodate(dst_page);
+
+			err =
+			    ssdfs_page_array_set_page_dirty(&desc->pebi->cache,
+							    *desc->cur_page);
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to set page %lu dirty: "
+					  "err %d\n",
+					  *desc->cur_page, err);
+			}
+		}
+
+		ssdfs_unlock_page(dst_page);
+		ssdfs_put_page(dst_page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("page %p, count %d\n",
+			  dst_page, page_ref_count(dst_page));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		if (err == -EAGAIN) {
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("try to get next page: "
+				  "write_offset %u, dst_free_space %zu\n",
+				  *desc->write_offset,
+				  dst_free_space);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			*desc->write_offset += dst_free_space;
+			(*desc->cur_page)++;
+			goto try_get_next_page;
+		}
+
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to store fragment: "
+				  "sequence_id %u, write_offset %u, err %d\n",
+				  desc->start_sequence_id + i,
+				  *desc->write_offset, err);
+			return err;
+		}
+
+		desc->uncompr_size += iter_bytes;
+		*desc->write_offset += to.compr_size;
+		desc->compr_size += to.compr_size;
+		desc->fragments_count++;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_store_blk_bmap_fragment() - store fragment of block bitmap
+ * @desc: descriptor of block bitmap fragment environment
+ * @bmap_hdr_offset: offset of header from log's beginning
+ *
+ * This function tries to store block bitmap fragment
+ * into PEB's log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_blk_bmap_fragment(struct ssdfs_bmap_descriptor *desc,
+				      u32 bmap_hdr_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_block_bitmap_fragment *frag_hdr = NULL;
+	struct ssdfs_fragment_desc *frag_desc_array = NULL;
+	size_t frag_hdr_size = sizeof(struct ssdfs_block_bitmap_fragment);
+	size_t frag_desc_size = sizeof(struct ssdfs_fragment_desc);
+	size_t allocation_size = 0;
+	u32 frag_hdr_off;
+	struct ssdfs_pagevec_descriptor pvec_desc;
+	u32 pages_per_peb;
+	struct page *page;
+	pgoff_t index;
+	u32 page_off;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!desc);
+	BUG_ON(!desc->pebi || !desc->cur_page || !desc->write_offset);
+	BUG_ON(ssdfs_page_vector_count(desc->snapshot) == 0);
+
+	switch (desc->compression_type) {
+	case SSDFS_BLK_BMAP_NOCOMPR_TYPE:
+	case SSDFS_BLK_BMAP_ZLIB_COMPR_TYPE:
+	case SSDFS_BLK_BMAP_LZO_COMPR_TYPE:
+		/* valid type */
+		break;
+
+	default:
+		SSDFS_WARN("invalid compression %#x\n",
+			   desc->compression_type);
+		return -EINVAL;
+	}
+
+	SSDFS_DBG("peb_id %llu, peb_index %u, "
+		  "cur_page %lu, write_offset %u, "
+		  "desc->compression_type %#x\n",
+		  desc->pebi->peb_id,
+		  desc->pebi->peb_index,
+		  *(desc->cur_page), *(desc->write_offset),
+		  desc->compression_type);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = desc->pebi->pebc->parent_si->fsi;
+
+	allocation_size = frag_hdr_size;
+	allocation_size +=
+		ssdfs_page_vector_count(desc->snapshot) * frag_desc_size;
+
+	frag_hdr = ssdfs_flush_kzalloc(allocation_size, GFP_KERNEL);
+	if (!frag_hdr) {
+		SSDFS_ERR("unable to allocate block bmap header\n");
+		return -ENOMEM;
+	}
+
+	frag_hdr_off = *(desc->write_offset);
+	*(desc->write_offset) += allocation_size;
+
+	frag_desc_array = (struct ssdfs_fragment_desc *)((u8 *)frag_hdr +
+							  frag_hdr_size);
+
+	switch (desc->compression_type) {
+	case SSDFS_BLK_BMAP_NOCOMPR_TYPE:
+		pvec_desc.compression_type = SSDFS_FRAGMENT_UNCOMPR_BLOB;
+		break;
+
+	case SSDFS_BLK_BMAP_ZLIB_COMPR_TYPE:
+		pvec_desc.compression_type = SSDFS_FRAGMENT_ZLIB_BLOB;
+		break;
+
+	case SSDFS_BLK_BMAP_LZO_COMPR_TYPE:
+		pvec_desc.compression_type = SSDFS_FRAGMENT_LZO_BLOB;
+		break;
+
+	default:
+		SSDFS_WARN("invalid compression %#x\n",
+			   desc->compression_type);
+		return -EINVAL;
+	}
+
+	pvec_desc.pebi = desc->pebi;
+	pvec_desc.start_sequence_id = 0;
+	pvec_desc.area_offset = bmap_hdr_offset;
+	pvec_desc.page_vec = desc->snapshot;
+	pvec_desc.bytes_count = desc->bytes_count;
+	pvec_desc.desc_array = frag_desc_array;
+	pvec_desc.array_capacity = SSDFS_BLK_BMAP_FRAGMENTS_CHAIN_MAX;
+	pvec_desc.cur_page = desc->cur_page;
+	pvec_desc.write_offset = desc->write_offset;
+
+	err = ssdfs_peb_store_pagevec(&pvec_desc);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to store block bitmap in the log: "
+			  "seg %llu, peb %llu, write_offset %u, "
+			  "err %d\n",
+			  desc->pebi->pebc->parent_si->seg_id,
+			  desc->pebi->peb_id,
+			  *(desc->write_offset), err);
+		goto fail_store_bmap_fragment;
+	}
+
+	frag_hdr->peb_index = cpu_to_le16(desc->peb_index);
+	frag_hdr->sequence_id = *(desc->frag_id);
+	*(desc->frag_id) += 1;
+	frag_hdr->flags = desc->flags;
+	frag_hdr->type = desc->type;
+
+	pages_per_peb = fsi->pages_per_peb;
+
+	if (desc->last_free_blk >= pages_per_peb) {
+		SSDFS_ERR("last_free_page %u >= pages_per_peb %u\n",
+			  desc->last_free_blk, pages_per_peb);
+		err = -ERANGE;
+		goto fail_store_bmap_fragment;
+	}
+
+	if ((desc->invalid_blks + desc->metadata_blks) > pages_per_peb) {
+		SSDFS_ERR("invalid descriptor state: "
+			  "invalid_blks %u, metadata_blks %u, "
+			  "pages_per_peb %u\n",
+			  desc->invalid_blks,
+			  desc->metadata_blks,
+			  pages_per_peb);
+		err = -ERANGE;
+		goto fail_store_bmap_fragment;
+	}
+
+	frag_hdr->last_free_blk = cpu_to_le32(desc->last_free_blk);
+	frag_hdr->metadata_blks = cpu_to_le32(desc->metadata_blks);
+	frag_hdr->invalid_blks = cpu_to_le32(desc->invalid_blks);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	WARN_ON(pvec_desc.compr_size > pvec_desc.uncompr_size);
+	WARN_ON(pvec_desc.compr_size >
+			desc->pebi->pebc->parent_si->fsi->segsize);
+#endif /* CONFIG_SSDFS_DEBUG */
+	frag_hdr->chain_hdr.compr_bytes = cpu_to_le32(pvec_desc.compr_size);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	WARN_ON(pvec_desc.uncompr_size >
+			desc->pebi->pebc->parent_si->fsi->segsize);
+#endif /* CONFIG_SSDFS_DEBUG */
+	frag_hdr->chain_hdr.uncompr_bytes = cpu_to_le32(pvec_desc.uncompr_size);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	WARN_ON(pvec_desc.fragments_count > SSDFS_BLK_BMAP_FRAGMENTS_CHAIN_MAX);
+#endif /* CONFIG_SSDFS_DEBUG */
+	frag_hdr->chain_hdr.fragments_count =
+		cpu_to_le16(pvec_desc.fragments_count);
+
+	frag_hdr->chain_hdr.desc_size = cpu_to_le16(frag_desc_size);
+	frag_hdr->chain_hdr.magic = SSDFS_CHAIN_HDR_MAGIC;
+	frag_hdr->chain_hdr.type = SSDFS_BLK_BMAP_CHAIN_HDR;
+	frag_hdr->chain_hdr.flags = 0;
+
+	index = ssdfs_write_offset_to_mem_page_index(fsi,
+				desc->pebi->current_log.start_page,
+				frag_hdr_off);
+
+	page = ssdfs_page_array_get_page_locked(&desc->pebi->cache, index);
+	if (IS_ERR_OR_NULL(page)) {
+		SSDFS_ERR("fail to get cache page: index %lu\n", index);
+		err = -ENOMEM;
+		goto fail_store_bmap_fragment;
+	}
+
+	page_off = frag_hdr_off % PAGE_SIZE;
+
+	err = ssdfs_memcpy_to_page(page, page_off, PAGE_SIZE,
+				   frag_hdr, 0, allocation_size,
+				   allocation_size);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to copy: "
+			  "page_off %u, allocation_size %zu, err %d\n",
+			  page_off, allocation_size, err);
+		goto finish_copy;
+	}
+
+	ssdfs_set_page_private(page, 0);
+	SetPageUptodate(page);
+
+	err = ssdfs_page_array_set_page_dirty(&desc->pebi->cache, index);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to set page %lu dirty: "
+			  "err %d\n",
+			  index, err);
+	}
+
+finish_copy:
+	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 */
+
+fail_store_bmap_fragment:
+	ssdfs_block_bmap_forget_snapshot(desc->snapshot);
+	ssdfs_flush_kfree(frag_hdr);
+	return err;
+}
+
+/*
+ * ssdfs_peb_store_dst_blk_bmap() - store destination block bitmap
+ * @pebi: pointer on PEB object
+ * @items_state: PEB container's items state
+ * @compression: compression type
+ * @bmap_hdr_off: offset from log's beginning to bitmap header
+ * @frag_id: pointer on fragments counter [in|out]
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store destination block bitmap
+ * into destination PEB's log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_dst_blk_bmap(struct ssdfs_peb_info *pebi,
+				 int items_state,
+				 u8 compression,
+				 u32 bmap_hdr_off,
+				 u8 *frag_id,
+				 pgoff_t *cur_page,
+				 u32 *write_offset)
+{
+	struct ssdfs_segment_blk_bmap *seg_blkbmap;
+	struct ssdfs_peb_blk_bmap *peb_blkbmap;
+	struct ssdfs_block_bmap *bmap;
+	struct ssdfs_bmap_descriptor desc;
+	int buffers_state;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!frag_id || !cur_page || !write_offset);
+	BUG_ON(!rwsem_is_locked(&pebi->pebc->lock));
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	switch (items_state) {
+	case SSDFS_PEB1_SRC_PEB2_DST_CONTAINER:
+	case SSDFS_PEB2_SRC_PEB1_DST_CONTAINER:
+		/* valid state */
+		break;
+
+	default:
+		SSDFS_WARN("invalid items_state %#x\n",
+			   items_state);
+		return -EINVAL;
+	}
+
+	switch (compression) {
+	case SSDFS_BLK_BMAP_NOCOMPR_TYPE:
+	case SSDFS_BLK_BMAP_ZLIB_COMPR_TYPE:
+	case SSDFS_BLK_BMAP_LZO_COMPR_TYPE:
+		/* valid type */
+		break;
+
+	default:
+		SSDFS_WARN("invalid compression %#x\n",
+			   compression);
+		return -EINVAL;
+	}
+
+	SSDFS_DBG("seg %llu, peb_index %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_index,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	desc.compression_type = compression;
+	desc.flags = SSDFS_PEB_HAS_RELATION | SSDFS_MIGRATING_BLK_BMAP;
+	desc.type = SSDFS_DST_BLK_BMAP;
+	desc.frag_id = frag_id;
+	desc.cur_page = cur_page;
+	desc.write_offset = write_offset;
+
+	desc.snapshot = &pebi->current_log.bmap_snapshot;
+
+	if (!pebi->pebc->src_peb || !pebi->pebc->dst_peb) {
+		SSDFS_WARN("empty src or dst PEB pointer\n");
+		return -ERANGE;
+	}
+
+	if (pebi == pebi->pebc->src_peb)
+		desc.pebi = pebi->pebc->src_peb;
+	else
+		desc.pebi = pebi->pebc->dst_peb;
+
+	if (!desc.pebi) {
+		SSDFS_WARN("destination PEB doesn't exist\n");
+		return -ERANGE;
+	}
+
+	desc.peb_index = desc.pebi->peb_index;
+
+	seg_blkbmap = &pebi->pebc->parent_si->blk_bmap;
+	peb_blkbmap = &seg_blkbmap->peb[pebi->pebc->peb_index];
+
+	err = ssdfs_page_vector_init(desc.snapshot);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to init page vector: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	if (!ssdfs_peb_blk_bmap_initialized(peb_blkbmap)) {
+		SSDFS_ERR("PEB's block bitmap isn't initialized\n");
+		return -ERANGE;
+	}
+
+	down_read(&peb_blkbmap->lock);
+
+	buffers_state = atomic_read(&peb_blkbmap->buffers_state);
+	switch (buffers_state) {
+	case SSDFS_PEB_BMAP1_SRC_PEB_BMAP2_DST:
+	case SSDFS_PEB_BMAP2_SRC_PEB_BMAP1_DST:
+		/* valid state */
+		break;
+
+	default:
+		err = -ERANGE;
+		SSDFS_WARN("invalid buffers_state %#x\n",
+			   buffers_state);
+		goto finish_store_dst_blk_bmap;
+	}
+
+	bmap = peb_blkbmap->dst;
+	if (!bmap) {
+		err = -ERANGE;
+		SSDFS_WARN("destination bitmap doesn't exist\n");
+		goto finish_store_dst_blk_bmap;
+	}
+
+	err = ssdfs_block_bmap_lock(bmap);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to lock block bitmap: err %d\n", err);
+		goto finish_store_dst_blk_bmap;
+	}
+
+	err = ssdfs_block_bmap_snapshot(bmap, desc.snapshot,
+					&desc.last_free_blk,
+					&desc.metadata_blks,
+					&desc.invalid_blks,
+					&desc.bytes_count);
+
+	ssdfs_block_bmap_unlock(bmap);
+
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to snapshot block bitmap: "
+			  "seg %llu, peb_index %u, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->pebc->peb_index, err);
+		goto finish_store_dst_blk_bmap;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("peb_id %llu, DST: last_free_blk %u, "
+		  "metadata_blks %u, invalid_blks %u\n",
+		  pebi->peb_id, desc.last_free_blk,
+		  desc.metadata_blks, desc.invalid_blks);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (ssdfs_page_vector_count(desc.snapshot) == 0) {
+		err = -ERANGE;
+		SSDFS_ERR("empty block bitmap\n");
+		goto finish_store_dst_blk_bmap;
+	}
+
+finish_store_dst_blk_bmap:
+	up_read(&peb_blkbmap->lock);
+
+	if (unlikely(err))
+		return err;
+
+	return ssdfs_peb_store_blk_bmap_fragment(&desc, bmap_hdr_off);
+}
+
+/*
+ * ssdfs_peb_store_source_blk_bmap() - store source block bitmap
+ * @pebi: pointer on PEB object
+ * @items_state: PEB container's items state
+ * @compression: compression type
+ * @bmap_hdr_off: offset from log's beginning to bitmap header
+ * @frag_id: pointer on fragments counter [in|out]
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store source block bitmap
+ * into destination PEB's log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_source_blk_bmap(struct ssdfs_peb_info *pebi,
+				    int items_state,
+				    u8 compression,
+				    u32 bmap_hdr_off,
+				    u8 *frag_id,
+				    pgoff_t *cur_page,
+				    u32 *write_offset)
+{
+	struct ssdfs_segment_blk_bmap *seg_blkbmap;
+	struct ssdfs_peb_blk_bmap *peb_blkbmap;
+	struct ssdfs_block_bmap *bmap;
+	struct ssdfs_bmap_descriptor desc;
+	int buffers_state;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!frag_id || !cur_page || !write_offset);
+	BUG_ON(!pebi);
+	BUG_ON(!rwsem_is_locked(&pebi->pebc->lock));
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	switch (items_state) {
+	case SSDFS_PEB1_SRC_CONTAINER:
+	case SSDFS_PEB2_SRC_CONTAINER:
+	case SSDFS_PEB1_SRC_PEB2_DST_CONTAINER:
+	case SSDFS_PEB2_SRC_PEB1_DST_CONTAINER:
+	case SSDFS_PEB1_DST_CONTAINER:
+	case SSDFS_PEB2_DST_CONTAINER:
+		/* valid state */
+		break;
+
+	default:
+		SSDFS_WARN("invalid items_state %#x\n",
+			   items_state);
+		return -EINVAL;
+	}
+
+	switch (compression) {
+	case SSDFS_BLK_BMAP_NOCOMPR_TYPE:
+	case SSDFS_BLK_BMAP_ZLIB_COMPR_TYPE:
+	case SSDFS_BLK_BMAP_LZO_COMPR_TYPE:
+		/* valid type */
+		break;
+
+	default:
+		SSDFS_WARN("invalid compression %#x\n",
+			   compression);
+		return -EINVAL;
+	}
+
+	SSDFS_DBG("seg %llu, peb_index %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_index,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	desc.compression_type = compression;
+	desc.frag_id = frag_id;
+	desc.cur_page = cur_page;
+	desc.write_offset = write_offset;
+
+	desc.snapshot = &pebi->current_log.bmap_snapshot;
+
+	switch (items_state) {
+	case SSDFS_PEB1_SRC_CONTAINER:
+	case SSDFS_PEB2_SRC_CONTAINER:
+		desc.flags = 0;
+		desc.type = SSDFS_SRC_BLK_BMAP;
+		desc.pebi = pebi->pebc->src_peb;
+		break;
+
+	case SSDFS_PEB1_SRC_PEB2_DST_CONTAINER:
+	case SSDFS_PEB2_SRC_PEB1_DST_CONTAINER:
+		if (!pebi->pebc->src_peb || !pebi->pebc->dst_peb) {
+			SSDFS_WARN("empty src or dst PEB pointer\n");
+			return -ERANGE;
+		}
+
+		desc.flags = SSDFS_PEB_HAS_RELATION |
+				SSDFS_MIGRATING_BLK_BMAP;
+		desc.type = SSDFS_SRC_BLK_BMAP;
+
+		if (pebi == pebi->pebc->src_peb)
+			desc.pebi = pebi->pebc->src_peb;
+		else
+			desc.pebi = pebi->pebc->dst_peb;
+		break;
+
+	case SSDFS_PEB1_DST_CONTAINER:
+	case SSDFS_PEB2_DST_CONTAINER:
+		desc.flags = SSDFS_MIGRATING_BLK_BMAP;
+		desc.type = SSDFS_DST_BLK_BMAP;
+		/* log could be created in destintaion PEB only */
+		desc.pebi = pebi->pebc->dst_peb;
+		break;
+
+	default:
+		BUG();
+	}
+
+	if (!desc.pebi) {
+		SSDFS_WARN("destination PEB doesn't exist\n");
+		return -ERANGE;
+	}
+
+	desc.peb_index = desc.pebi->peb_index;
+
+	seg_blkbmap = &pebi->pebc->parent_si->blk_bmap;
+	peb_blkbmap = &seg_blkbmap->peb[pebi->peb_index];
+
+	err = ssdfs_page_vector_init(desc.snapshot);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to init page vector: "
+			  "err %d\n", err);
+		return err;
+	}
+
+	if (!ssdfs_peb_blk_bmap_initialized(peb_blkbmap)) {
+		SSDFS_ERR("PEB's block bitmap isn't initialized\n");
+		return -ERANGE;
+	}
+
+	down_read(&peb_blkbmap->lock);
+
+	buffers_state = atomic_read(&peb_blkbmap->buffers_state);
+	switch (buffers_state) {
+	case SSDFS_PEB_BMAP1_SRC:
+	case SSDFS_PEB_BMAP2_SRC:
+	case SSDFS_PEB_BMAP1_SRC_PEB_BMAP2_DST:
+	case SSDFS_PEB_BMAP2_SRC_PEB_BMAP1_DST:
+		/* valid state */
+		break;
+
+	default:
+		err = -ERANGE;
+		SSDFS_WARN("invalid buffers_state %#x\n",
+			   buffers_state);
+		goto finish_store_src_blk_bmap;
+	}
+
+	bmap = peb_blkbmap->src;
+	if (!bmap) {
+		err = -ERANGE;
+		SSDFS_WARN("source bitmap doesn't exist\n");
+		goto finish_store_src_blk_bmap;
+	}
+
+	err = ssdfs_block_bmap_lock(bmap);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to lock block bitmap: err %d\n", err);
+		goto finish_store_src_blk_bmap;
+	}
+
+	err = ssdfs_block_bmap_snapshot(bmap, desc.snapshot,
+					&desc.last_free_blk,
+					&desc.metadata_blks,
+					&desc.invalid_blks,
+					&desc.bytes_count);
+
+	ssdfs_block_bmap_unlock(bmap);
+
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to snapshot block bitmap: "
+			  "seg %llu, peb_index %u, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->pebc->peb_index, err);
+		goto finish_store_src_blk_bmap;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("peb_id %llu, SRC: last_free_blk %u, "
+		  "metadata_blks %u, invalid_blks %u\n",
+		  pebi->peb_id, desc.last_free_blk,
+		  desc.metadata_blks, desc.invalid_blks);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (desc.metadata_blks == 0) {
+		SSDFS_WARN("peb_id %llu, SRC: last_free_blk %u, "
+			   "metadata_blks %u, invalid_blks %u\n",
+			   pebi->peb_id, desc.last_free_blk,
+			   desc.metadata_blks, desc.invalid_blks);
+		BUG();
+	}
+
+	if (ssdfs_page_vector_count(desc.snapshot) == 0) {
+		err = -ERANGE;
+		SSDFS_ERR("empty block bitmap\n");
+		goto finish_store_src_blk_bmap;
+	}
+
+finish_store_src_blk_bmap:
+	up_read(&peb_blkbmap->lock);
+
+	if (unlikely(err))
+		return err;
+
+	return ssdfs_peb_store_blk_bmap_fragment(&desc, bmap_hdr_off);
+}
+
+/*
+ * ssdfs_peb_store_dependent_blk_bmap() - store dependent source bitmaps
+ * @pebi: pointer on PEB object
+ * @items_state: PEB container's items state
+ * @compression: compression type
+ * @bmap_hdr_off: offset from log's beginning to bitmap header
+ * @frag_id: pointer on fragments counter [in|out]
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store dependent source block bitmaps
+ * of migrating PEBs into destination PEB's log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_dependent_blk_bmap(struct ssdfs_peb_info *pebi,
+					int items_state,
+					u8 compression,
+					u32 bmap_hdr_off,
+					u8 *frag_id,
+					pgoff_t *cur_page,
+					u32 *write_offset)
+{
+	struct ssdfs_segment_blk_bmap *seg_blkbmap;
+	struct ssdfs_peb_blk_bmap *peb_blkbmap;
+	struct ssdfs_block_bmap *bmap;
+	struct ssdfs_bmap_descriptor desc;
+	int i;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!frag_id || !cur_page || !write_offset);
+	BUG_ON(!rwsem_is_locked(&pebi->pebc->lock));
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	switch (items_state) {
+	case SSDFS_PEB1_SRC_PEB2_DST_CONTAINER:
+	case SSDFS_PEB2_SRC_PEB1_DST_CONTAINER:
+	case SSDFS_PEB1_DST_CONTAINER:
+	case SSDFS_PEB2_DST_CONTAINER:
+		/* valid state */
+		break;
+
+	default:
+		SSDFS_WARN("invalid items_state %#x\n",
+			   items_state);
+		return -EINVAL;
+	}
+
+	switch (compression) {
+	case SSDFS_BLK_BMAP_NOCOMPR_TYPE:
+	case SSDFS_BLK_BMAP_ZLIB_COMPR_TYPE:
+	case SSDFS_BLK_BMAP_LZO_COMPR_TYPE:
+		/* valid type */
+		break;
+
+	default:
+		SSDFS_WARN("invalid compression %#x\n",
+			   compression);
+		return -EINVAL;
+	}
+
+	SSDFS_DBG("seg %llu, peb_index %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_index,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	desc.compression_type = compression;
+	desc.frag_id = frag_id;
+	desc.cur_page = cur_page;
+	desc.write_offset = write_offset;
+
+	desc.snapshot = &pebi->current_log.bmap_snapshot;
+
+	switch (items_state) {
+	case SSDFS_PEB1_SRC_PEB2_DST_CONTAINER:
+	case SSDFS_PEB2_SRC_PEB1_DST_CONTAINER:
+	case SSDFS_PEB1_DST_CONTAINER:
+	case SSDFS_PEB2_DST_CONTAINER:
+		desc.flags = SSDFS_PEB_HAS_EXT_PTR | SSDFS_MIGRATING_BLK_BMAP;
+		desc.type = SSDFS_SRC_BLK_BMAP;
+		desc.pebi = pebi->pebc->dst_peb;
+		break;
+
+	default:
+		BUG();
+	}
+
+	if (!desc.pebi) {
+		SSDFS_WARN("destination PEB doesn't exist\n");
+		return -ERANGE;
+	}
+
+	seg_blkbmap = &pebi->pebc->parent_si->blk_bmap;
+
+	for (i = 0; i < pebi->pebc->parent_si->pebs_count; i++) {
+		struct ssdfs_peb_container *cur_pebc;
+		struct ssdfs_peb_info *dst_peb;
+		int buffers_state;
+
+		cur_pebc = &pebi->pebc->parent_si->peb_array[i];
+
+		switch (atomic_read(&cur_pebc->items_state)) {
+		case SSDFS_PEB1_SRC_EXT_PTR_DST_CONTAINER:
+		case SSDFS_PEB2_SRC_EXT_PTR_DST_CONTAINER:
+			/* do nothing here */
+			break;
+
+		default:
+			continue;
+		};
+
+		down_read(&cur_pebc->lock);
+		dst_peb = cur_pebc->dst_peb;
+		up_read(&cur_pebc->lock);
+
+		if (dst_peb == NULL) {
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("dst_peb is NULL: "
+				  "peb_index %u\n",
+				  i);
+#endif /* CONFIG_SSDFS_DEBUG */
+			continue;
+		} else if (dst_peb != pebi->pebc->dst_peb)
+			continue;
+
+		peb_blkbmap = &seg_blkbmap->peb[i];
+
+		err = ssdfs_page_vector_init(desc.snapshot);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to init page vector: "
+				  "err %d\n", err);
+			return err;
+		}
+
+		desc.peb_index = (u16)i;
+
+		if (!ssdfs_peb_blk_bmap_initialized(peb_blkbmap)) {
+			SSDFS_ERR("PEB's block bitmap isn't initialized\n");
+			return -ERANGE;
+		}
+
+		down_read(&peb_blkbmap->lock);
+
+		buffers_state = atomic_read(&peb_blkbmap->buffers_state);
+		switch (buffers_state) {
+		case SSDFS_PEB_BMAP1_SRC:
+		case SSDFS_PEB_BMAP2_SRC:
+			/* valid state */
+			break;
+
+		default:
+			err = -ERANGE;
+			SSDFS_WARN("invalid buffers_state %#x\n",
+				   buffers_state);
+			goto finish_store_dependent_blk_bmap;
+		}
+
+		bmap = peb_blkbmap->src;
+		if (!bmap) {
+			err = -ERANGE;
+			SSDFS_WARN("source bitmap doesn't exist\n");
+			goto finish_store_dependent_blk_bmap;
+		}
+
+		err = ssdfs_block_bmap_lock(bmap);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to lock block bitmap: err %d\n", err);
+			goto finish_store_dependent_blk_bmap;
+		}
+
+		err = ssdfs_block_bmap_snapshot(bmap, desc.snapshot,
+						&desc.last_free_blk,
+						&desc.metadata_blks,
+						&desc.invalid_blks,
+						&desc.bytes_count);
+
+		ssdfs_block_bmap_unlock(bmap);
+
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to snapshot block bitmap: "
+				  "seg %llu, peb_index %u, err %d\n",
+				  cur_pebc->parent_si->seg_id,
+				  cur_pebc->peb_index, err);
+			goto finish_store_dependent_blk_bmap;
+		}
+
+		if (ssdfs_page_vector_count(desc.snapshot) == 0) {
+			err = -ERANGE;
+			SSDFS_ERR("empty block bitmap\n");
+			goto finish_store_dependent_blk_bmap;
+		}
+
+finish_store_dependent_blk_bmap:
+		up_read(&peb_blkbmap->lock);
+
+		if (unlikely(err))
+			return err;
+
+		err = ssdfs_peb_store_blk_bmap_fragment(&desc, bmap_hdr_off);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to store block bitmap fragment: "
+				  "peb_index %u, err %d\n",
+				  i, err);
+			return err;
+		}
+
+		ssdfs_block_bmap_forget_snapshot(desc.snapshot);
+	}
+
+	return 0;
+}
+
+static inline
+void ssdfs_prepare_blk_bmap_options(struct ssdfs_fs_info *fsi,
+				    u16 *flags, u8 *compression)
+{
+	u8 type;
+
+	*flags = fsi->metadata_options.blk_bmap.flags;
+	type = fsi->metadata_options.blk_bmap.compression;
+
+	*compression = SSDFS_BLK_BMAP_UNCOMPRESSED_BLOB;
+
+	if (*flags & SSDFS_BLK_BMAP_MAKE_COMPRESSION) {
+		switch (type) {
+		case SSDFS_BLK_BMAP_NOCOMPR_TYPE:
+			*compression = SSDFS_BLK_BMAP_UNCOMPRESSED_BLOB;
+			break;
+
+		case SSDFS_BLK_BMAP_ZLIB_COMPR_TYPE:
+			*compression = SSDFS_BLK_BMAP_ZLIB_BLOB;
+			break;
+
+		case SSDFS_BLK_BMAP_LZO_COMPR_TYPE:
+			*compression = SSDFS_BLK_BMAP_LZO_BLOB;
+			break;
+		}
+	}
+}
+
+/*
+ * ssdfs_peb_store_block_bmap() - store block bitmap into page cache
+ * @pebi: pointer on PEB object
+ * @desc: block bitmap descriptor [out]
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store block bitmap into log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_block_bmap(struct ssdfs_peb_info *pebi,
+				struct ssdfs_metadata_descriptor *desc,
+				pgoff_t *cur_page,
+				u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_segment_blk_bmap *seg_blkbmap;
+	struct ssdfs_block_bitmap_header *bmap_hdr = NULL;
+	size_t bmap_hdr_size = sizeof(struct ssdfs_block_bitmap_header);
+	int items_state;
+	u8 frag_id = 0;
+	u32 bmap_hdr_off;
+	u32 pages_per_peb;
+	u16 log_start_page = 0;
+	u16 flags = 0;
+	u8 compression = SSDFS_BLK_BMAP_UNCOMPRESSED_BLOB;
+	struct page *page;
+	pgoff_t index;
+	void *kaddr;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(flags & ~SSDFS_BLK_BMAP_FLAG_MASK);
+	BUG_ON(!desc || !cur_page || !write_offset);
+
+	SSDFS_DBG("seg %llu, peb_index %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_index,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	seg_blkbmap = &pebi->pebc->parent_si->blk_bmap;
+
+	pages_per_peb = min_t(u32, fsi->leb_pages_capacity,
+				   fsi->peb_pages_capacity);
+
+	ssdfs_prepare_blk_bmap_options(fsi, &flags, &compression);
+
+	bmap_hdr_off = *write_offset;
+	*write_offset += bmap_hdr_size;
+
+	items_state = atomic_read(&pebi->pebc->items_state);
+	switch (items_state) {
+	case SSDFS_PEB1_SRC_CONTAINER:
+	case SSDFS_PEB2_SRC_CONTAINER:
+		/* Prepare source bitmap only */
+		err = ssdfs_peb_store_source_blk_bmap(pebi, items_state,
+						      compression,
+						      bmap_hdr_off,
+						      &frag_id,
+						      cur_page,
+						      write_offset);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to store source bitmap: "
+				  "cur_page %lu, write_offset %u, "
+				  "err %d\n",
+				  *cur_page, *write_offset, err);
+			goto finish_store_block_bitmap;
+		}
+		break;
+
+	case SSDFS_PEB1_SRC_PEB2_DST_CONTAINER:
+	case SSDFS_PEB2_SRC_PEB1_DST_CONTAINER:
+		if (!pebi->pebc->src_peb || !pebi->pebc->dst_peb) {
+			err = -ERANGE;
+			SSDFS_WARN("invalid src or dst PEB pointer\n");
+			goto finish_store_block_bitmap;
+		}
+
+		/*
+		 * Prepare
+		 * (1) destination bitmap
+		 * (2) source bitmap
+		 * (3) all dependent bitmaps
+		 */
+		err = ssdfs_peb_store_dst_blk_bmap(pebi, items_state,
+						   compression,
+						   bmap_hdr_off,
+						   &frag_id,
+						   cur_page,
+						   write_offset);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to store destination bitmap: "
+				  "cur_page %lu, write_offset %u, "
+				  "err %d\n",
+				  *cur_page, *write_offset, err);
+			goto finish_store_block_bitmap;
+		}
+
+		err = ssdfs_peb_store_source_blk_bmap(pebi, items_state,
+						      compression,
+						      bmap_hdr_off,
+						      &frag_id,
+						      cur_page,
+						      write_offset);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to store source bitmap: "
+				  "cur_page %lu, write_offset %u, "
+				  "err %d\n",
+				  *cur_page, *write_offset, err);
+			goto finish_store_block_bitmap;
+		}
+
+		err = ssdfs_peb_store_dependent_blk_bmap(pebi, items_state,
+							 compression,
+							 bmap_hdr_off,
+							 &frag_id,
+							 cur_page,
+							 write_offset);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to store dependent bitmaps: "
+				  "cur_page %lu, write_offset %u, "
+				  "err %d\n",
+				  *cur_page, *write_offset, err);
+			goto finish_store_block_bitmap;
+		}
+		break;
+
+	case SSDFS_PEB1_DST_CONTAINER:
+	case SSDFS_PEB2_DST_CONTAINER:
+		/*
+		 * Prepare
+		 * (1) source bitmap
+		 * (2) all dependent bitmaps
+		 */
+		err = ssdfs_peb_store_source_blk_bmap(pebi, items_state,
+						      compression,
+						      bmap_hdr_off,
+						      &frag_id,
+						      cur_page,
+						      write_offset);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to store source bitmap: "
+				  "cur_page %lu, write_offset %u, "
+				  "err %d\n",
+				  *cur_page, *write_offset, err);
+			goto finish_store_block_bitmap;
+		}
+
+		err = ssdfs_peb_store_dependent_blk_bmap(pebi, items_state,
+							 compression,
+							 bmap_hdr_off,
+							 &frag_id,
+							 cur_page,
+							 write_offset);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to store dependent bitmaps: "
+				  "cur_page %lu, write_offset %u, "
+				  "err %d\n",
+				  *cur_page, *write_offset, err);
+			goto finish_store_block_bitmap;
+		}
+		break;
+
+	default:
+		err = -ERANGE;
+		SSDFS_WARN("invalid items_state %#x\n",
+			   items_state);
+		break;
+	}
+
+	if (pebi->current_log.start_page >= pages_per_peb) {
+		err = -ERANGE;
+		SSDFS_ERR("log_start_page %u >= pages_per_peb %u\n",
+			  log_start_page, pages_per_peb);
+		goto finish_store_block_bitmap;
+	}
+
+	desc->offset = cpu_to_le32(bmap_hdr_off +
+			    (pebi->current_log.start_page * fsi->pagesize));
+
+	index = ssdfs_write_offset_to_mem_page_index(fsi,
+					     pebi->current_log.start_page,
+					     bmap_hdr_off);
+
+	page = ssdfs_page_array_get_page_locked(&pebi->cache, index);
+	if (IS_ERR_OR_NULL(page)) {
+		SSDFS_ERR("fail to get cache page: index %lu\n", index);
+		err = -ENOMEM;
+		goto finish_store_block_bitmap;
+	}
+
+	kaddr = kmap_local_page(page);
+
+	bmap_hdr = SSDFS_BLKBMP_HDR((u8 *)kaddr +
+				    (bmap_hdr_off % PAGE_SIZE));
+
+	bmap_hdr->magic.common = cpu_to_le32(SSDFS_SUPER_MAGIC);
+	bmap_hdr->magic.key = cpu_to_le16(SSDFS_BLK_BMAP_MAGIC);
+	bmap_hdr->magic.version.major = SSDFS_MAJOR_REVISION;
+	bmap_hdr->magic.version.minor = SSDFS_MINOR_REVISION;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(frag_id == 0);
+#endif /* CONFIG_SSDFS_DEBUG */
+	bmap_hdr->fragments_count = cpu_to_le16(frag_id);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(*write_offset <= bmap_hdr_off);
+	BUG_ON(*write_offset <= (bmap_hdr_off + bmap_hdr_size));
+#endif /* CONFIG_SSDFS_DEBUG */
+	bmap_hdr->bytes_count = cpu_to_le32(*write_offset - bmap_hdr_off);
+	desc->size = bmap_hdr->bytes_count;
+
+	pebi->current_log.prev_log_bmap_bytes =
+			le32_to_cpu(bmap_hdr->bytes_count);
+
+	bmap_hdr->flags = flags;
+	bmap_hdr->type = compression;
+
+	desc->check.bytes = cpu_to_le16(bmap_hdr_size);
+
+	switch (compression) {
+	case SSDFS_BLK_BMAP_ZLIB_BLOB:
+		desc->check.flags = cpu_to_le16(SSDFS_CRC32 |
+						SSDFS_ZLIB_COMPRESSED);
+		break;
+
+	case SSDFS_BLK_BMAP_LZO_BLOB:
+		desc->check.flags = cpu_to_le16(SSDFS_CRC32 |
+						SSDFS_LZO_COMPRESSED);
+		break;
+
+	default:
+		desc->check.flags = cpu_to_le16(SSDFS_CRC32);
+		break;
+	}
+
+	err = ssdfs_calculate_csum(&desc->check, bmap_hdr, bmap_hdr_size);
+	if (unlikely(err)) {
+		SSDFS_ERR("unable to calculate checksum: err %d\n", err);
+		goto finish_bmap_hdr_preparation;
+	}
+
+	pebi->current_log.seg_flags |= SSDFS_SEG_HDR_HAS_BLK_BMAP;
+
+finish_bmap_hdr_preparation:
+	flush_dcache_page(page);
+	kunmap_local(kaddr);
+
+	ssdfs_set_page_private(page, 0);
+	SetPageUptodate(page);
+
+	err = ssdfs_page_array_set_page_dirty(&pebi->cache, index);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to set page %lu dirty: "
+			  "err %d\n",
+			  index, err);
+	}
+
+	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 */
+
+finish_store_block_bitmap:
+	return err;
+}
+
+/*
+ * is_peb_area_empty() - check that PEB's area is empty
+ * @pebi: pointer on PEB object
+ * @area_type: type of area
+ */
+static inline
+bool is_peb_area_empty(struct ssdfs_peb_info *pebi, int area_type)
+{
+	struct ssdfs_peb_area *area;
+	size_t blk_table_size = sizeof(struct ssdfs_area_block_table);
+	bool is_empty = false;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	area = &pebi->current_log.area[area_type];
+
+	if (area->has_metadata)
+		is_empty = area->write_offset == blk_table_size;
+	else
+		is_empty = area->write_offset == 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("area_type %#x, write_offset %u, is_empty %d\n",
+		  area_type, area->write_offset, (int)is_empty);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	return is_empty;
+}
+
+/*
+ * ssdfs_peb_copy_area_pages_into_cache() - copy area pages into cache
+ * @pebi: pointer on PEB object
+ * @area_type: type of area
+ * @desc: descriptor of metadata area
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to copy area pages into log's page cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ENODATA    - area is empty.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_copy_area_pages_into_cache(struct ssdfs_peb_info *pebi,
+					 int area_type,
+					 struct ssdfs_metadata_descriptor *desc,
+					 pgoff_t *cur_page,
+					 u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_peb_area *area;
+	size_t blk_table_size = sizeof(struct ssdfs_area_block_table);
+	struct pagevec pvec;
+	struct ssdfs_page_array *smap, *dmap;
+	pgoff_t page_index, end, pages_count, range_len;
+	struct page *page;
+	u32 area_offset, area_size = 0;
+	u16 log_start_page;
+	u32 read_bytes = 0;
+	u32 area_write_offset = 0;
+	u16 flags;
+	int i;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(area_type >= SSDFS_LOG_AREA_MAX);
+	BUG_ON(!desc || !cur_page || !write_offset);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	area = &pebi->current_log.area[area_type];
+	log_start_page = pebi->current_log.start_page;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "area_type %#x, area->write_offset %u, "
+		  "area->compressed_offset %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  area_type, area->write_offset,
+		  area->compressed_offset,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (is_peb_area_empty(pebi, area_type)) {
+		SSDFS_DBG("area %#x is empty\n", area_type);
+		return -ENODATA;
+	}
+
+	smap = &area->array;
+	dmap = &pebi->cache;
+
+	switch (area_type) {
+	case SSDFS_LOG_BLK_DESC_AREA:
+		flags = fsi->metadata_options.blk2off_tbl.flags;
+		if (flags & SSDFS_BLK2OFF_TBL_MAKE_COMPRESSION)
+			area_write_offset = area->compressed_offset;
+		else
+			area_write_offset = area->write_offset;
+		break;
+
+	default:
+		area_write_offset = area->write_offset;
+		break;
+	}
+
+	area_offset = *write_offset;
+	area_size = area_write_offset;
+
+	desc->offset = cpu_to_le32(area_offset +
+					(log_start_page * fsi->pagesize));
+	desc->size = cpu_to_le32(area_size);
+
+	if (area->has_metadata) {
+		void *kaddr;
+		u8 compression = fsi->metadata_options.blk2off_tbl.compression;
+		u16 metadata_flags = SSDFS_CRC32;
+
+		switch (area_type) {
+		case SSDFS_LOG_BLK_DESC_AREA:
+			flags = fsi->metadata_options.blk2off_tbl.flags;
+			if (flags & SSDFS_BLK2OFF_TBL_MAKE_COMPRESSION) {
+				switch (compression) {
+				case SSDFS_BLK2OFF_TBL_ZLIB_COMPR_TYPE:
+					metadata_flags |= SSDFS_ZLIB_COMPRESSED;
+					break;
+
+				case SSDFS_BLK2OFF_TBL_LZO_COMPR_TYPE:
+					metadata_flags |= SSDFS_LZO_COMPRESSED;
+					break;
+
+				default:
+					/* do nothing */
+					break;
+				}
+			}
+			break;
+
+		default:
+			/* do nothing */
+			break;
+		}
+
+		page = ssdfs_page_array_get_page_locked(smap, 0);
+		if (IS_ERR_OR_NULL(page)) {
+			SSDFS_ERR("fail to get page of area %#x\n",
+				  area_type);
+			return -ERANGE;
+		}
+
+		kaddr = kmap_local_page(page);
+		desc->check.bytes = cpu_to_le16(blk_table_size);
+		desc->check.flags = cpu_to_le16(metadata_flags);
+		err = ssdfs_calculate_csum(&desc->check, kaddr, blk_table_size);
+		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 (unlikely(err)) {
+			SSDFS_ERR("unable to calculate checksum: err %d\n",
+				  err);
+			return err;
+		}
+
+		err = ssdfs_page_array_set_page_dirty(smap, 0);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to set page dirty: err %d\n",
+				  err);
+			return err;
+		}
+	}
+
+	pagevec_init(&pvec);
+
+	page_index = 0;
+	pages_count = area_write_offset + PAGE_SIZE - 1;
+	pages_count >>= PAGE_SHIFT;
+
+	while (page_index < pages_count) {
+		int i;
+
+		range_len = min_t(pgoff_t,
+				  (pgoff_t)PAGEVEC_SIZE,
+				  (pgoff_t)(pages_count - page_index));
+		end = page_index + range_len - 1;
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("page_index %lu, pages_count %lu\n",
+			  page_index, pages_count);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		err = ssdfs_page_array_lookup_range(smap, &page_index, end,
+						    SSDFS_DIRTY_PAGE_TAG,
+						    PAGEVEC_SIZE,
+						    &pvec);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to find any dirty pages: err %d\n",
+				  err);
+			return err;
+		}
+
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			struct page *page1 = pvec.pages[i], *page2;
+			pgoff_t src_index = page1->index;
+			u32 src_len, dst_len, copy_len;
+			u32 src_off, dst_off;
+			u32 rest_len = PAGE_SIZE;
+
+			if (read_bytes == area_size)
+				goto finish_pagevec_copy;
+			else if (read_bytes > area_size) {
+				err = -E2BIG;
+				SSDFS_ERR("too many pages: "
+					  "pages_count %u, area_size %u\n",
+					  pagevec_count(&pvec),
+					  area_size);
+				goto finish_current_copy;
+			}
+
+			src_off = 0;
+
+try_copy_area_data:
+			ssdfs_lock_page(page1);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page1, page_ref_count(page1));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			if (*write_offset >= PAGE_SIZE)
+				dst_off = *write_offset % PAGE_SIZE;
+			else
+				dst_off = *write_offset;
+
+			src_len = min_t(u32, area_size - read_bytes, rest_len);
+			dst_len = min_t(u32, PAGE_SIZE, PAGE_SIZE - dst_off);
+			copy_len = min_t(u32, src_len, dst_len);
+
+			page2 = ssdfs_page_array_grab_page(dmap, *cur_page);
+			if (unlikely(IS_ERR_OR_NULL(page2))) {
+				err = -ENOMEM;
+				SSDFS_ERR("fail to grab page: index %lu\n",
+					  *cur_page);
+				goto unlock_page1;
+			}
+
+			err = ssdfs_memcpy_page(page2, dst_off, PAGE_SIZE,
+						page1, src_off, PAGE_SIZE,
+						copy_len);
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to copy: "
+					  "src_off %u, dst_off %u, "
+					  "copy_len %u\n",
+					  src_off, dst_off, copy_len);
+				goto unlock_page2;
+			}
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("src_off %u, dst_off %u, src_len %u, "
+				  "dst_len %u, copy_len %u, "
+				  "write_offset %u, cur_page %lu, "
+				  "page_index %d\n",
+				  src_off, dst_off, src_len, dst_len, copy_len,
+				  *write_offset, *cur_page, i);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			if (PageDirty(page1)) {
+				err = ssdfs_page_array_set_page_dirty(dmap,
+								     *cur_page);
+				if (unlikely(err)) {
+					SSDFS_ERR("fail to set page dirty: "
+						  "page_index %lu, err %d\n",
+						  *cur_page, err);
+					goto unlock_page2;
+				}
+			} else {
+				err = -ERANGE;
+				SSDFS_ERR("page %d is not dirty\n", i);
+				goto unlock_page2;
+			}
+
+unlock_page2:
+			ssdfs_unlock_page(page2);
+			ssdfs_put_page(page2);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page2, page_ref_count(page2));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+unlock_page1:
+			ssdfs_unlock_page(page1);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page1, page_ref_count(page1));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+finish_current_copy:
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to copy page: "
+					  " from %lu to %lu, err %d\n",
+					  src_index, *cur_page, err);
+				goto fail_copy_area_pages;
+			}
+
+			read_bytes += copy_len;
+			*write_offset += copy_len;
+			rest_len -= copy_len;
+
+			if ((dst_off + copy_len) >= PAGE_SIZE)
+				++(*cur_page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("read_bytes %u, area_size %u, "
+				  "write_offset %u, copy_len %u, rest_len %u\n",
+				  read_bytes, area_size,
+				  *write_offset, copy_len, rest_len);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			if (read_bytes == area_size) {
+				err = ssdfs_page_array_clear_dirty_page(smap,
+								page_index + i);
+				if (unlikely(err)) {
+					SSDFS_ERR("fail to mark page clean: "
+						  "page_index %lu\n",
+						  page_index + i);
+					goto fail_copy_area_pages;
+				} else
+					goto finish_pagevec_copy;
+			} else if ((src_off + copy_len) < PAGE_SIZE) {
+				src_off += copy_len;
+				goto try_copy_area_data;
+			} else {
+				err = ssdfs_page_array_clear_dirty_page(smap,
+								page_index + i);
+				if (unlikely(err)) {
+					SSDFS_ERR("fail to mark page clean: "
+						  "page_index %lu\n",
+						  page_index + i);
+					goto fail_copy_area_pages;
+				}
+			}
+		}
+
+finish_pagevec_copy:
+		page_index += PAGEVEC_SIZE;
+
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			page = pvec.pages[i];
+			ssdfs_put_page(page);
+		}
+
+		pagevec_reinit(&pvec);
+		cond_resched();
+	};
+
+	err = ssdfs_page_array_release_all_pages(smap);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to release area's pages: "
+			  "err %d\n", err);
+		goto finish_copy_area_pages;
+	}
+
+	pebi->current_log.seg_flags |= SSDFS_AREA_TYPE2FLAG(area_type);
+
+	return 0;
+
+fail_copy_area_pages:
+	for (i = 0; i < pagevec_count(&pvec); i++) {
+		page = pvec.pages[i];
+		ssdfs_put_page(page);
+	}
+
+	pagevec_reinit(&pvec);
+
+finish_copy_area_pages:
+	return err;
+}
+
+/*
+ * ssdfs_peb_move_area_pages_into_cache() - move area pages into cache
+ * @pebi: pointer on PEB object
+ * @area_type: type of area
+ * @desc: descriptor of metadata area
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to move area pages into log's page cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ENODATA    - area is empty.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_move_area_pages_into_cache(struct ssdfs_peb_info *pebi,
+					 int area_type,
+					 struct ssdfs_metadata_descriptor *desc,
+					 pgoff_t *cur_page,
+					 u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_peb_area *area;
+	size_t blk_table_size = sizeof(struct ssdfs_area_block_table);
+	struct pagevec pvec;
+	struct ssdfs_page_array *smap, *dmap;
+	pgoff_t page_index, end, pages_count, range_len;
+	struct page *page;
+	u32 area_offset, area_size;
+	void *kaddr;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(area_type >= SSDFS_LOG_AREA_MAX);
+	BUG_ON(!desc || !cur_page || !write_offset);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	area = &pebi->current_log.area[area_type];
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "area_type %#x, area->write_offset %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  area_type, area->write_offset,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (is_peb_area_empty(pebi, area_type)) {
+		SSDFS_DBG("area %#x is empty\n", area_type);
+		return -ENODATA;
+	}
+
+	smap = &area->array;
+	dmap = &pebi->cache;
+
+	area_offset = *write_offset;
+	area_size = area->write_offset;
+
+	desc->offset = cpu_to_le32(area_offset +
+				(pebi->current_log.start_page * fsi->pagesize));
+
+	desc->size = cpu_to_le32(area_size);
+
+	if (area->has_metadata) {
+		page = ssdfs_page_array_get_page_locked(smap, 0);
+		if (IS_ERR_OR_NULL(page)) {
+			SSDFS_ERR("fail to get page of area %#x\n",
+				  area_type);
+			return -ERANGE;
+		}
+
+		kaddr = kmap_local_page(page);
+		desc->check.bytes = cpu_to_le16(blk_table_size);
+		desc->check.flags = cpu_to_le16(SSDFS_CRC32);
+		err = ssdfs_calculate_csum(&desc->check, kaddr, blk_table_size);
+		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 (unlikely(err)) {
+			SSDFS_ERR("unable to calculate checksum: err %d\n",
+				  err);
+			return err;
+		}
+
+		err = ssdfs_page_array_set_page_dirty(smap, 0);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to set page dirty: err %d\n",
+				  err);
+			return err;
+		}
+	}
+
+	pagevec_init(&pvec);
+
+	page_index = 0;
+	pages_count = area->write_offset + PAGE_SIZE - 1;
+	pages_count >>= PAGE_SHIFT;
+
+	while (page_index < pages_count) {
+		int i;
+
+		range_len = min_t(pgoff_t,
+				  (pgoff_t)PAGEVEC_SIZE,
+				  (pgoff_t)(pages_count - page_index));
+		end = page_index + range_len - 1;
+
+		err = ssdfs_page_array_lookup_range(smap, &page_index, end,
+						    SSDFS_DIRTY_PAGE_TAG,
+						    PAGEVEC_SIZE,
+						    &pvec);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to find any dirty pages: err %d\n",
+				  err);
+			return err;
+		}
+
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			struct page *page = pvec.pages[i], *page2;
+			pgoff_t src_off = page->index;
+
+			ssdfs_lock_page(page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("page %p, count %d\n",
+				  page, page_ref_count(page));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			page2 = ssdfs_page_array_delete_page(smap, src_off);
+			if (IS_ERR_OR_NULL(page2)) {
+				err = !page2 ? -ERANGE : PTR_ERR(page2);
+				SSDFS_ERR("fail to delete page %lu: err %d\n",
+					  src_off, err);
+				goto finish_current_move;
+			}
+
+			WARN_ON(page2 != page);
+
+#ifdef CONFIG_SSDFS_DEBUG
+			SSDFS_DBG("cur_page %lu, write_offset %u, "
+				  "i %d, pvec_count %u\n",
+				  *cur_page, *write_offset,
+				  i, pagevec_count(&pvec));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			page->index = *cur_page;
+
+			err = ssdfs_page_array_add_page(dmap, page, *cur_page);
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to add page %lu: err %d\n",
+					  *cur_page, err);
+				goto finish_current_move;
+			}
+
+			if (PageDirty(page)) {
+				err = ssdfs_page_array_set_page_dirty(dmap,
+								     *cur_page);
+				if (unlikely(err)) {
+					SSDFS_ERR("fail to set page dirty: "
+						  "page_index %lu, err %d\n",
+						  *cur_page, err);
+					goto finish_current_move;
+				}
+			} else {
+				err = -ERANGE;
+				SSDFS_ERR("page %d is not dirty\n", i);
+				goto finish_current_move;
+			}
+
+			pvec.pages[i] = NULL;
+
+finish_current_move:
+			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 (unlikely(err)) {
+				for (i = 0; i < pagevec_count(&pvec); i++) {
+					page = pvec.pages[i];
+					if (!page)
+						continue;
+					ssdfs_put_page(page);
+				}
+
+				pagevec_reinit(&pvec);
+				SSDFS_ERR("fail to move page: "
+					  " from %lu to %lu, err %d\n",
+					  src_off, *cur_page, err);
+				return err;
+			}
+
+			(*cur_page)++;
+			*write_offset += PAGE_SIZE;
+		}
+
+		page_index += PAGEVEC_SIZE;
+
+		pagevec_reinit(&pvec);
+		cond_resched();
+	};
+
+	pebi->current_log.seg_flags |= SSDFS_AREA_TYPE2FLAG(area_type);
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_store_blk_desc_table() - try to store block descriptor table
+ * @pebi: pointer on PEB object
+ * @desc: descriptor of metadata area
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store block descriptor into log's page cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ENODATA    - area is empty.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_blk_desc_table(struct ssdfs_peb_info *pebi,
+				   struct ssdfs_metadata_descriptor *desc,
+				   pgoff_t *cur_page,
+				   u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_peb_area *area;
+	struct ssdfs_fragment_desc *meta_desc;
+	struct ssdfs_fragments_chain_header *chain_hdr;
+	struct ssdfs_peb_temp_buffer *buf;
+	int area_type = SSDFS_LOG_BLK_DESC_AREA;
+	u16 flags;
+	size_t uncompr_size;
+	size_t compr_size = 0;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!desc || !cur_page || !write_offset);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	area = &pebi->current_log.area[area_type];
+	chain_hdr = &area->metadata.area.blk_desc.table.chain_hdr;
+	buf = &area->metadata.area.blk_desc.flush_buf;
+	flags = fsi->metadata_options.blk2off_tbl.flags;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "area->write_offset %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  area->write_offset,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (is_peb_area_empty(pebi, area_type)) {
+		SSDFS_DBG("area %#x is empty\n", area_type);
+		return -ENODATA;
+	}
+
+	meta_desc = ssdfs_peb_get_area_cur_frag_desc(pebi, area_type);
+	if (IS_ERR(meta_desc)) {
+		SSDFS_ERR("fail to get current fragment descriptor: "
+			  "err %d\n",
+			  (int)PTR_ERR(meta_desc));
+		return PTR_ERR(meta_desc);
+	} else if (!meta_desc) {
+		err = -ERANGE;
+		SSDFS_ERR("fail to get current fragment descriptor: "
+			  "err %d\n",
+			  err);
+		return err;
+	}
+
+	uncompr_size = le32_to_cpu(meta_desc->uncompr_size);
+
+	if (uncompr_size == 0) {
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("latest fragment of blk desc table is empty: "
+			  "seg %llu, peb %llu, current_log.start_page %u, "
+			  "area->write_offset %u, "
+			  "cur_page %lu, write_offset %u\n",
+			  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+			  pebi->current_log.start_page,
+			  area->write_offset,
+			  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+		goto copy_area_pages_into_cache;
+	}
+
+	if (flags & SSDFS_BLK2OFF_TBL_MAKE_COMPRESSION) {
+#ifdef CONFIG_SSDFS_DEBUG
+		BUG_ON(!buf->ptr);
+
+		if (buf->write_offset >= buf->size) {
+			SSDFS_ERR("invalid request: "
+				  "buf->write_offset %u, buf->size %zu\n",
+				  buf->write_offset, buf->size);
+			return -ERANGE;
+		}
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		meta_desc->flags = SSDFS_FRAGMENT_HAS_CSUM;
+
+		if (uncompr_size > buf->size) {
+			SSDFS_ERR("invalid state: "
+				  "uncompr_size %zu > buf->size %zu\n",
+				  uncompr_size, buf->size);
+			return -ERANGE;
+		}
+
+		meta_desc->checksum = ssdfs_crc32_le(buf->ptr, uncompr_size);
+
+		if (le32_to_cpu(meta_desc->checksum) == 0) {
+			SSDFS_WARN("checksum is invalid: "
+				   "seg %llu, peb %llu, bytes_count %zu\n",
+				   pebi->pebc->parent_si->seg_id,
+				   pebi->peb_id,
+				   uncompr_size);
+			return -ERANGE;
+		}
+
+		err = ssdfs_peb_compress_blk_descs_fragment(pebi,
+							    uncompr_size,
+							    &compr_size);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to compress blk desc fragment: "
+				  "err %d\n", err);
+			return err;
+		}
+
+		meta_desc->offset = cpu_to_le32(area->compressed_offset);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		WARN_ON(compr_size > U16_MAX);
+#endif /* CONFIG_SSDFS_DEBUG */
+		meta_desc->compr_size = cpu_to_le16((u16)compr_size);
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("offset %u, compr_size %u, "
+			  "uncompr_size %u, checksum %#x\n",
+			  le32_to_cpu(meta_desc->offset),
+			  le16_to_cpu(meta_desc->compr_size),
+			  le16_to_cpu(meta_desc->uncompr_size),
+			  le32_to_cpu(meta_desc->checksum));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		area->compressed_offset += compr_size;
+		le32_add_cpu(&chain_hdr->compr_bytes, compr_size);
+	}
+
+	err = ssdfs_peb_store_area_block_table(pebi, area_type, 0);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to store area's block table: "
+			  "area %#x, err %d\n",
+			  area_type, err);
+		return err;
+	}
+
+copy_area_pages_into_cache:
+	err = ssdfs_peb_copy_area_pages_into_cache(pebi, area_type,
+						   desc, cur_page,
+						   write_offset);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to move pages in the cache: err %d\n",
+			  err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_store_log_footer() - store log footer
+ * @pebi: pointer on PEB object
+ * @flags: log footer's flags
+ * @hdr_desc: log footer's metadata descriptor in header
+ * @lf_desc: log footer's metadata descriptors array
+ * @array_size: count of items in array
+ * @cur_segs: current segment IDs array
+ * @cur_segs_size: size of segment IDs array size in bytes
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store log footer into PEB's page cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ENOMEM     - fail to allocate memory page.
+ */
+static
+int ssdfs_peb_store_log_footer(struct ssdfs_peb_info *pebi,
+				u32 flags,
+				struct ssdfs_metadata_descriptor *hdr_desc,
+				struct ssdfs_metadata_descriptor *lf_desc,
+				size_t array_size,
+				__le64 *cur_segs,
+				size_t cur_segs_size,
+				pgoff_t *cur_page,
+				u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_log_footer *footer;
+	size_t desc_size = sizeof(struct ssdfs_metadata_descriptor);
+	size_t array_bytes = desc_size * array_size;
+	int padding;
+	u32 log_pages;
+	struct page *page;
+	u32 area_offset, area_size;
+	u64 last_log_time;
+	u64 last_log_cno;
+	int i;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!hdr_desc || !lf_desc || !cur_segs);
+	BUG_ON(!cur_page || !write_offset);
+	BUG_ON(array_size != SSDFS_LOG_FOOTER_DESC_MAX);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	area_offset = *write_offset;
+	area_size = sizeof(struct ssdfs_log_footer);
+
+	*write_offset += max_t(u32, PAGE_SIZE, area_size);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(flags & ~SSDFS_LOG_FOOTER_FLAG_MASK);
+	BUG_ON(((*write_offset + fsi->pagesize - 1) >> fsi->log_pagesize) >
+		pebi->log_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	log_pages = (*write_offset + fsi->pagesize - 1) / fsi->pagesize;
+
+	padding = *cur_page % pebi->log_pages;
+	padding = pebi->log_pages - padding;
+	padding--;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("area_offset %u, write_offset %u, "
+		  "log_pages %u, padding %d, "
+		  "cur_page %lu\n",
+		  area_offset, *write_offset,
+		  log_pages, padding,
+		  *cur_page);
+
+	if (padding > 1) {
+		SSDFS_WARN("padding is big: "
+			   "seg %llu, peb %llu, current_log.start_page %u, "
+			   "cur_page %lu, write_offset %u, "
+			   "padding %d\n",
+			   pebi->pebc->parent_si->seg_id,
+			   pebi->peb_id,
+			   pebi->current_log.start_page,
+			   *cur_page, *write_offset,
+			   padding);
+	}
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (padding > 0) {
+		/*
+		 * Align the log_pages and log_bytes.
+		 */
+		log_pages += padding;
+		*write_offset = log_pages * fsi->pagesize;
+		area_offset = *write_offset - fsi->pagesize;
+
+		for (i = 0; i < padding; i++) {
+			page = ssdfs_page_array_grab_page(&pebi->cache,
+							  *cur_page);
+			if (IS_ERR_OR_NULL(page)) {
+				SSDFS_ERR("fail to get cache page: index %lu\n",
+					  *cur_page);
+				return -ENOMEM;
+			}
+
+			ssdfs_memset_page(page, 0, PAGE_SIZE, 0xFF, PAGE_SIZE);
+
+			ssdfs_set_page_private(page, 0);
+			SetPageUptodate(page);
+
+			err = ssdfs_page_array_set_page_dirty(&pebi->cache,
+							      *cur_page);
+			if (unlikely(err)) {
+				SSDFS_ERR("fail to set page dirty: "
+					  "page_index %lu, err %d\n",
+					  *cur_page, err);
+			}
+
+			ssdfs_unlock_page(page);
+			ssdfs_put_page(page);
+
+			if (unlikely(err))
+				return err;
+
+			(*cur_page)++;
+		}
+	}
+
+	page = ssdfs_page_array_grab_page(&pebi->cache, *cur_page);
+	if (IS_ERR_OR_NULL(page)) {
+		SSDFS_ERR("fail to get cache page: index %lu\n",
+			  *cur_page);
+		return -ENOMEM;
+	}
+
+	footer = kmap_local_page(page);
+	memset(footer, 0xFF, PAGE_SIZE);
+	ssdfs_memcpy(footer->desc_array, 0, array_bytes,
+		     lf_desc, 0, array_bytes,
+		     array_bytes);
+
+	last_log_time = pebi->current_log.last_log_time;
+	last_log_cno = pebi->current_log.last_log_cno;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(pebi->peb_create_time > last_log_time);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_prepare_volume_state_info_for_commit(fsi, SSDFS_MOUNTED_FS,
+							 cur_segs,
+							 cur_segs_size,
+							 last_log_time,
+							 last_log_cno,
+							 &footer->volume_state);
+
+	if (!err) {
+		err = ssdfs_prepare_log_footer_for_commit(fsi, log_pages,
+							  flags,
+							  last_log_time,
+							  last_log_cno,
+							  footer);
+
+		footer->peb_create_time = cpu_to_le64(pebi->peb_create_time);
+	}
+
+	if (!err) {
+		hdr_desc->offset = cpu_to_le32(area_offset +
+				(pebi->current_log.start_page * fsi->pagesize));
+		hdr_desc->size = cpu_to_le32(area_size);
+
+		ssdfs_memcpy(&hdr_desc->check,
+			     0, sizeof(struct ssdfs_metadata_check),
+			     &footer->volume_state.check,
+			     0, sizeof(struct ssdfs_metadata_check),
+			     sizeof(struct ssdfs_metadata_check));
+	}
+
+	flush_dcache_page(page);
+	kunmap_local(footer);
+
+	ssdfs_set_page_private(page, 0);
+	SetPageUptodate(page);
+
+	err = ssdfs_page_array_set_page_dirty(&pebi->cache, *cur_page);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to set page dirty: "
+			  "page_index %lu, err %d\n",
+			  *cur_page, err);
+	}
+
+	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 (unlikely(err)) {
+		SSDFS_CRIT("fail to store log footer: "
+			   "seg %llu, peb %llu, current_log.start_page %u, "
+			   "err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id,
+			   pebi->current_log.start_page, err);
+		return err;
+	}
+
+	pebi->current_log.seg_flags |= SSDFS_LOG_HAS_FOOTER;
+
+	(*cur_page)++;
+
+	return 0;
+}
+
+/*
+ * ssdfs_extract_src_peb_migration_id() - prepare src PEB's migration_id
+ * @pebi: pointer on PEB object
+ * @prev_id: pointer on previous PEB's peb_migration_id [out]
+ * @cur_id: pointer on current PEB's peb_migration_id [out]
+ */
+static inline
+int ssdfs_extract_src_peb_migration_id(struct ssdfs_peb_info *pebi,
+					u8 *prev_id, u8 *cur_id)
+{
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc || !pebi->pebc->src_peb);
+	BUG_ON(!prev_id || !cur_id);
+	BUG_ON(!rwsem_is_locked(&pebi->pebc->lock));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*prev_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+	*cur_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+
+	if (pebi != pebi->pebc->src_peb) {
+		SSDFS_ERR("pebi %p != src_peb %p\n",
+			  pebi, pebi->pebc->src_peb);
+		return -ERANGE;
+	}
+
+	*cur_id = ssdfs_get_peb_migration_id_checked(pebi);
+	if (unlikely(*cur_id < 0)) {
+		err = *cur_id;
+		*cur_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+		SSDFS_ERR("fail to get migration_id: "
+			  "seg %llu, peb %llu, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  err);
+		return err;
+	}
+
+	*prev_id = ssdfs_define_prev_peb_migration_id(pebi);
+	if (!is_peb_migration_id_valid(*prev_id)) {
+		err = *prev_id;
+		*prev_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+		SSDFS_ERR("fail to define prev migration_id: "
+			  "seg %llu, peb %llu, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  err);
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_extract_dst_peb_migration_id() - prepare dst PEB's migration_id
+ * @pebi: pointer on PEB object
+ * @prev_id: pointer on previous PEB's peb_migration_id [out]
+ * @cur_id: pointer on current PEB's peb_migration_id [out]
+ */
+static inline
+int ssdfs_extract_dst_peb_migration_id(struct ssdfs_peb_info *pebi,
+					u8 *prev_id, u8 *cur_id)
+{
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->src_peb || !pebi->pebc->dst_peb);
+	BUG_ON(!prev_id || !cur_id);
+	BUG_ON(!rwsem_is_locked(&pebi->pebc->lock));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	*prev_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+	*cur_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+
+	*cur_id = ssdfs_get_peb_migration_id_checked(pebi->pebc->dst_peb);
+	if (unlikely(*cur_id < 0)) {
+		err = *cur_id;
+		*cur_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+		SSDFS_ERR("fail to get migration_id: "
+			  "seg %llu, peb %llu, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  err);
+		return err;
+	}
+
+	*prev_id = ssdfs_get_peb_migration_id_checked(pebi->pebc->src_peb);
+	if (unlikely(*prev_id < 0)) {
+		err = *prev_id;
+		*prev_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+		SSDFS_ERR("fail to get migration_id: "
+			  "seg %llu, peb %llu, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_store_peb_migration_id() - store peb_migration_id into header
+ * @pebi: pointer on PEB object
+ * @hdr: pointer on segment header [out]
+ */
+static
+int ssdfs_store_peb_migration_id(struct ssdfs_peb_info *pebi,
+				 struct ssdfs_segment_header *hdr)
+{
+	int items_state;
+	u8 prev_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+	u8 cur_id = SSDFS_PEB_UNKNOWN_MIGRATION_ID;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!rwsem_is_locked(&pebi->pebc->lock));
+
+	SSDFS_DBG("seg %llu, peb %llu\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	items_state = atomic_read(&pebi->pebc->items_state);
+	switch (items_state) {
+	case SSDFS_PEB1_SRC_CONTAINER:
+	case SSDFS_PEB2_SRC_CONTAINER:
+#ifdef CONFIG_SSDFS_DEBUG
+		BUG_ON(!pebi->pebc->src_peb || pebi->pebc->dst_peb);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		err = ssdfs_extract_src_peb_migration_id(pebi,
+							 &prev_id,
+							 &cur_id);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to extract peb_migration_id: "
+				  "seg %llu, peb %llu, err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id, err);
+			return err;
+		}
+		break;
+
+	case SSDFS_PEB1_DST_CONTAINER:
+	case SSDFS_PEB2_DST_CONTAINER:
+#ifdef CONFIG_SSDFS_DEBUG
+		BUG_ON(pebi->pebc->src_peb || !pebi->pebc->dst_peb);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		if (pebi != pebi->pebc->dst_peb) {
+			SSDFS_ERR("pebi %p != dst_peb %p\n",
+				  pebi, pebi->pebc->dst_peb);
+			return -ERANGE;
+		}
+
+		cur_id = ssdfs_get_peb_migration_id_checked(pebi);
+		if (unlikely(cur_id < 0)) {
+			err = cur_id;
+			SSDFS_ERR("fail to get migration_id: "
+				  "seg %llu, peb %llu, err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id,
+				  err);
+			return err;
+		}
+		break;
+
+	case SSDFS_PEB1_SRC_PEB2_DST_CONTAINER:
+	case SSDFS_PEB2_SRC_PEB1_DST_CONTAINER:
+#ifdef CONFIG_SSDFS_DEBUG
+		BUG_ON(!pebi->pebc->src_peb || !pebi->pebc->dst_peb);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		err = -ERANGE;
+
+		if (pebi == pebi->pebc->src_peb) {
+			err = ssdfs_extract_src_peb_migration_id(pebi,
+								 &prev_id,
+								 &cur_id);
+		} else if (pebi == pebi->pebc->dst_peb) {
+			err = ssdfs_extract_dst_peb_migration_id(pebi,
+								 &prev_id,
+								 &cur_id);
+		}
+
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to extract peb_migration_id: "
+				  "seg %llu, peb %llu, err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id, err);
+			return err;
+		}
+		break;
+
+	case SSDFS_PEB1_SRC_EXT_PTR_DST_CONTAINER:
+	case SSDFS_PEB2_SRC_EXT_PTR_DST_CONTAINER:
+#ifdef CONFIG_SSDFS_DEBUG
+		BUG_ON(!pebi->pebc->src_peb || !pebi->pebc->dst_peb);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		err = ssdfs_extract_src_peb_migration_id(pebi,
+							 &prev_id,
+							 &cur_id);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to extract peb_migration_id: "
+				  "seg %llu, peb %llu, err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id, err);
+			return err;
+		}
+		break;
+
+	default:
+		SSDFS_WARN("invalid items_state %#x\n",
+			   items_state);
+#ifdef CONFIG_SSDFS_DEBUG
+		BUG();
+#endif /* CONFIG_SSDFS_DEBUG */
+		return -ERANGE;
+	}
+
+	hdr->peb_migration_id[SSDFS_PREV_MIGRATING_PEB] = prev_id;
+	hdr->peb_migration_id[SSDFS_CUR_MIGRATING_PEB] = cur_id;
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_store_log_header() - store log's header
+ * @pebi: pointer on PEB object
+ * @desc_array: pointer on descriptors array
+ * @array_size: count of items in array
+ * @write_offset: current write offset in log
+ *
+ * This function tries to store log's header in PEB's page cache.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_log_header(struct ssdfs_peb_info *pebi,
+				struct ssdfs_metadata_descriptor *desc_array,
+				size_t array_size,
+				u32 write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_segment_header *hdr;
+	struct page *page;
+	size_t desc_size = sizeof(struct ssdfs_metadata_descriptor);
+	size_t array_bytes = desc_size * array_size;
+	u32 seg_flags;
+	u32 log_pages;
+	u16 seg_type;
+	u64 last_log_time;
+	u64 last_log_cno;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!desc_array);
+	BUG_ON(array_size != SSDFS_SEG_HDR_DESC_MAX);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(pebi->pebc->parent_si->seg_type > SSDFS_LAST_KNOWN_SEG_TYPE);
+	BUG_ON(pebi->current_log.seg_flags & ~SSDFS_SEG_HDR_FLAG_MASK);
+	BUG_ON(write_offset % fsi->pagesize);
+	BUG_ON((write_offset >> fsi->log_pagesize) > pebi->log_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	seg_type = pebi->pebc->parent_si->seg_type;
+	log_pages = pebi->log_pages;
+	seg_flags = pebi->current_log.seg_flags;
+
+	page = ssdfs_page_array_get_page_locked(&pebi->cache,
+						pebi->current_log.start_page);
+	if (IS_ERR_OR_NULL(page)) {
+		SSDFS_ERR("fail to get cache page: index %u\n",
+			  pebi->current_log.start_page);
+		return -ERANGE;
+	}
+
+	hdr = kmap_local_page(page);
+
+	ssdfs_memcpy(hdr->desc_array, 0, array_bytes,
+		     desc_array, 0, array_bytes,
+		     array_bytes);
+
+	ssdfs_create_volume_header(fsi, &hdr->volume_hdr);
+
+	err = ssdfs_prepare_volume_header_for_commit(fsi, &hdr->volume_hdr);
+	if (unlikely(err))
+		goto finish_segment_header_preparation;
+
+	err = ssdfs_store_peb_migration_id(pebi, hdr);
+	if (unlikely(err))
+		goto finish_segment_header_preparation;
+
+	hdr->peb_create_time = cpu_to_le64(pebi->peb_create_time);
+
+	last_log_time = pebi->current_log.last_log_time;
+	last_log_cno = pebi->current_log.last_log_cno;
+
+#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,
+		  last_log_time);
+
+	BUG_ON(pebi->peb_create_time > last_log_time);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_prepare_segment_header_for_commit(fsi,
+						      log_pages,
+						      seg_type,
+						      seg_flags,
+						      last_log_time,
+						      last_log_cno,
+						      hdr);
+	if (unlikely(err))
+		goto finish_segment_header_preparation;
+
+finish_segment_header_preparation:
+	flush_dcache_page(page);
+	kunmap_local(hdr);
+	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 (unlikely(err)) {
+		SSDFS_CRIT("fail to store segment header: "
+			   "seg %llu, peb %llu, current_log.start_page %u, "
+			   "err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id,
+			   pebi->current_log.start_page, err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_flush_current_log_dirty_pages() - flush log's dirty pages
+ * @pebi: pointer on PEB object
+ * @write_offset: current write offset in log
+ *
+ * This function tries to flush the current log's dirty pages.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_flush_current_log_dirty_pages(struct ssdfs_peb_info *pebi,
+					    u32 write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	loff_t peb_offset;
+	struct pagevec pvec;
+	u32 log_bytes, written_bytes;
+	u32 log_start_off;
+	unsigned flushed_pages;
+#ifdef CONFIG_SSDFS_CHECK_LOGICAL_BLOCK_EMPTYNESS
+	u32 pages_per_block;
+#endif /* CONFIG_SSDFS_CHECK_LOGICAL_BLOCK_EMPTYNESS */
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(write_offset == 0);
+	BUG_ON(write_offset % pebi->pebc->parent_si->fsi->pagesize);
+	BUG_ON(!pebi->pebc->parent_si->fsi->devops);
+	BUG_ON(!pebi->pebc->parent_si->fsi->devops->writepages);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	pagevec_init(&pvec);
+
+	peb_offset = (pebi->peb_id * fsi->pages_per_peb) << fsi->log_pagesize;
+
+	log_bytes = write_offset;
+	log_start_off = pebi->current_log.start_page << fsi->log_pagesize;
+	written_bytes = 0;
+	flushed_pages = 0;
+
+	while (written_bytes < log_bytes) {
+		pgoff_t index, end;
+		unsigned i;
+		u32 page_start_off, write_size;
+		loff_t iter_write_offset;
+		u32 pagevec_bytes;
+		pgoff_t written_pages = 0;
+
+		index = pebi->current_log.start_page + flushed_pages;
+		end = (pgoff_t)pebi->current_log.start_page + pebi->log_pages;
+		end = min_t(pgoff_t, end, (pgoff_t)(index + PAGEVEC_SIZE));
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("index %lu, end %lu\n",
+			  index, end);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		err = ssdfs_page_array_lookup_range(&pebi->cache,
+						    &index, end,
+						    SSDFS_DIRTY_PAGE_TAG,
+						    PAGEVEC_SIZE,
+						    &pvec);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to find dirty pages: "
+				  "index %lu, end %lu, err %d\n",
+				  index, end, err);
+			return -ERANGE;
+		}
+
+		page_start_off = log_start_off + written_bytes;
+		page_start_off %= PAGE_SIZE;
+
+		pagevec_bytes = (u32)pagevec_count(&pvec) * PAGE_SIZE;
+
+		write_size = min_t(u32,
+				   pagevec_bytes - page_start_off,
+				   log_bytes - written_bytes);
+
+		if ((written_bytes + write_size) > log_bytes) {
+			pagevec_reinit(&pvec);
+			SSDFS_ERR("written_bytes %u > log_bytes %u\n",
+				  written_bytes + write_size,
+				  log_bytes);
+			return -ERANGE;
+		}
+
+#ifdef CONFIG_SSDFS_DEBUG
+		BUG_ON(write_size % fsi->pagesize);
+		BUG_ON(written_bytes % fsi->pagesize);
+
+		for (i = 1; i < pagevec_count(&pvec); i++) {
+			struct page *page1, *page2;
+
+			page1 = pvec.pages[i - 1];
+			page2 = pvec.pages[i];
+
+			if ((page_index(page1) + 1) != page_index(page2)) {
+				SSDFS_ERR("not contiguous log: "
+					  "page_index1 %lu, page_index2 %lu\n",
+					  page_index(page1),
+					  page_index(page2));
+			}
+		}
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		iter_write_offset = peb_offset + log_start_off;
+		iter_write_offset += written_bytes;
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("iter_write_offset %llu, write_size %u, "
+			  "page_start_off %u\n",
+			  iter_write_offset, write_size, page_start_off);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+#ifdef CONFIG_SSDFS_CHECK_LOGICAL_BLOCK_EMPTYNESS
+		pages_per_block = fsi->pagesize / PAGE_SIZE;
+		for (i = 0; i < pagevec_count(&pvec); i += pages_per_block) {
+			u64 byte_off;
+
+			if (!fsi->devops->can_write_page) {
+				SSDFS_DBG("can_write_page is not supported\n");
+				break;
+			}
+
+			byte_off = iter_write_offset;
+			byte_off += i * PAGE_SIZE;
+
+			err = fsi->devops->can_write_page(fsi->sb, byte_off,
+							  true);
+			if (err) {
+				pagevec_reinit(&pvec);
+				ssdfs_fs_error(fsi->sb,
+					__FILE__, __func__, __LINE__,
+					"offset %llu err %d\n",
+					byte_off, err);
+				return err;
+			}
+		}
+#endif /* CONFIG_SSDFS_CHECK_LOGICAL_BLOCK_EMPTYNESS */
+
+		err = fsi->devops->writepages(fsi->sb, iter_write_offset,
+						&pvec,
+						page_start_off,
+						write_size);
+		if (unlikely(err)) {
+			pagevec_reinit(&pvec);
+			SSDFS_ERR("fail to flush pagevec: "
+				  "iter_write_offset %llu, write_size %u, "
+				  "err %d\n",
+				  iter_write_offset, write_size, err);
+			return err;
+		}
+
+		written_pages = write_size / PAGE_SIZE;
+
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			struct page *page = pvec.pages[i];
+
+#ifdef CONFIG_SSDFS_DEBUG
+			BUG_ON(!page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+			if (i < written_pages) {
+				ssdfs_lock_page(page);
+				ClearPageUptodate(page);
+				ssdfs_clear_page_private(page, 0);
+				pvec.pages[i] = NULL;
+				ssdfs_unlock_page(page);
+			} else {
+				ssdfs_lock_page(page);
+				pvec.pages[i] = NULL;
+				ssdfs_unlock_page(page);
+			}
+		}
+
+		end = index + written_pages - 1;
+		err = ssdfs_page_array_clear_dirty_range(&pebi->cache,
+							 index,
+							 end);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to clean dirty pages: "
+				  "start %lu, end %lu, err %d\n",
+				  index, end, err);
+		}
+
+		err = ssdfs_page_array_release_pages(&pebi->cache,
+						     &index, end);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to release pages: "
+				  "seg_id %llu, peb_id %llu, "
+				  "start %lu, end %lu, err %d\n",
+				  pebi->pebc->parent_si->seg_id,
+				  pebi->peb_id, index, end, err);
+		}
+
+		written_bytes += write_size;
+		flushed_pages += written_pages;
+
+		pagevec_reinit(&pvec);
+		cond_resched();
+	};
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_commit_log_payload() - commit payload of the log
+ * @pebi: pointer on PEB object
+ * @hdr_desc: log header's metadata descriptors array
+ * @log_has_data: does log contain data? [out]
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ */
+static
+int ssdfs_peb_commit_log_payload(struct ssdfs_peb_info *pebi,
+				 struct ssdfs_metadata_descriptor *hdr_desc,
+				 bool *log_has_data,
+				 pgoff_t *cur_page, u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_metadata_descriptor *cur_hdr_desc;
+	int area_type;
+	int err = 0;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!hdr_desc || !cur_page || !write_offset || !log_has_data);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u, "
+		  "cur_page %lu, write_offset %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page,
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+	*log_has_data = false;
+
+	cur_hdr_desc = &hdr_desc[SSDFS_BLK_BMAP_INDEX];
+	err = ssdfs_peb_store_block_bmap(pebi, cur_hdr_desc,
+					 cur_page, write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to store block bitmap: "
+			   "seg %llu, peb %llu, cur_page %lu, write_offset %u, "
+			   "err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id,
+			   *cur_page, *write_offset, err);
+		goto finish_commit_payload;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0001-payload: cur_page %lu, write_offset %u\n",
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	cur_hdr_desc = &hdr_desc[SSDFS_OFF_TABLE_INDEX];
+	err = ssdfs_peb_store_offsets_table(pebi, cur_hdr_desc,
+					    cur_page, write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to store offsets table: "
+			   "seg %llu, peb %llu, cur_page %lu, write_offset %u, "
+			   "err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id,
+			   *cur_page, *write_offset, err);
+		goto finish_commit_payload;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0002-payload: cur_page %lu, write_offset %u\n",
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	area_type = SSDFS_LOG_BLK_DESC_AREA;
+	cur_hdr_desc = &hdr_desc[SSDFS_AREA_TYPE2INDEX(area_type)];
+	err = ssdfs_peb_store_blk_desc_table(pebi, cur_hdr_desc,
+					     cur_page, write_offset);
+	if (err == -ENODATA) {
+		err = 0;
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("block descriptor area is absent: "
+			   "seg %llu, peb %llu, "
+			   "cur_page %lu, write_offset %u\n",
+			   pebi->pebc->parent_si->seg_id,
+			   pebi->peb_id,
+			   *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+	} else if (unlikely(err)) {
+		SSDFS_CRIT("fail to store block descriptors table: "
+			   "seg %llu, peb %llu, cur_page %lu, write_offset %u, "
+			   "err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id,
+			   *cur_page, *write_offset, err);
+		goto finish_commit_payload;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0003-payload: cur_page %lu, write_offset %u\n",
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	area_type = SSDFS_LOG_DIFFS_AREA;
+	cur_hdr_desc = &hdr_desc[SSDFS_AREA_TYPE2INDEX(area_type)];
+	err = ssdfs_peb_copy_area_pages_into_cache(pebi,
+						   area_type,
+						   cur_hdr_desc,
+						   cur_page,
+						   write_offset);
+	if (err == -ENODATA) {
+		err = 0;
+	} else if (unlikely(err)) {
+		SSDFS_CRIT("fail to move the area %d into PEB cache: "
+			   "seg %llu, peb %llu, cur_page %lu, "
+			   "write_offset %u, err %d\n",
+			   area_type, pebi->pebc->parent_si->seg_id,
+			   pebi->peb_id, *cur_page, *write_offset,
+			   err);
+		goto finish_commit_payload;
+	} else
+		*log_has_data = true;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0004-payload: cur_page %lu, write_offset %u\n",
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	area_type = SSDFS_LOG_JOURNAL_AREA;
+	cur_hdr_desc = &hdr_desc[SSDFS_AREA_TYPE2INDEX(area_type)];
+	err = ssdfs_peb_copy_area_pages_into_cache(pebi,
+						   area_type,
+						   cur_hdr_desc,
+						   cur_page,
+						   write_offset);
+	if (err == -ENODATA) {
+		err = 0;
+	} else if (unlikely(err)) {
+		SSDFS_CRIT("fail to move the area %d into PEB cache: "
+			   "seg %llu, peb %llu, cur_page %lu, "
+			   "write_offset %u, err %d\n",
+			   area_type, pebi->pebc->parent_si->seg_id,
+			   pebi->peb_id, *cur_page, *write_offset,
+			   err);
+		goto finish_commit_payload;
+	} else
+		*log_has_data = true;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0005-payload: cur_page %lu, write_offset %u\n",
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (*write_offset % PAGE_SIZE) {
+		(*cur_page)++;
+
+		*write_offset += PAGE_SIZE - 1;
+		*write_offset >>= PAGE_SHIFT;
+		*write_offset <<= PAGE_SHIFT;
+	}
+
+	area_type = SSDFS_LOG_MAIN_AREA;
+	cur_hdr_desc = &hdr_desc[SSDFS_AREA_TYPE2INDEX(area_type)];
+	err = ssdfs_peb_move_area_pages_into_cache(pebi,
+						   area_type,
+						   cur_hdr_desc,
+						   cur_page,
+						   write_offset);
+	if (err == -ENODATA) {
+		err = 0;
+	} else if (unlikely(err)) {
+		SSDFS_CRIT("fail to move the area %d into PEB cache: "
+			   "seg %llu, peb %llu, cur_page %lu, "
+			   "write_offset %u, err %d\n",
+			   area_type, pebi->pebc->parent_si->seg_id,
+			   pebi->peb_id, *cur_page, *write_offset,
+			   err);
+		goto finish_commit_payload;
+	} else
+		*log_has_data = true;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0006-payload: cur_page %lu, write_offset %u\n",
+		  *cur_page, *write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+finish_commit_payload:
+	return err;
+}
+
 /*
  * ssdfs_peb_define_next_log_start() - define start of the next log
  * @pebi: pointer on PEB object
