diff --git a/fs/ssdfs/peb_flush_thread.c b/fs/ssdfs/peb_flush_thread.c
index 6a9032762ea6..d9352804f6b9 100644
--- a/fs/ssdfs/peb_flush_thread.c
+++ b/fs/ssdfs/peb_flush_thread.c
@@ -113,6 +113,1751 @@ void ssdfs_flush_check_memory_leaks(void)
  *                         FLUSH THREAD FUNCTIONALITY                         *
  ******************************************************************************/
 
+/*
+ * ssdfs_peb_define_next_log_start() - define start of the next log
+ * @pebi: pointer on PEB object
+ * @log_strategy: strategy in log creation
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ */
+static
+void ssdfs_peb_define_next_log_start(struct ssdfs_peb_info *pebi,
+				     int log_strategy,
+				     pgoff_t *cur_page, u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	u16 pages_diff;
+	u16 rest_phys_free_pages = 0;
+	u32 pages_per_peb;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!cur_page || !write_offset);
+	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, log_strategy %#x, "
+		  "current_log.start_page %u, "
+		  "cur_page %lu, write_offset %u, "
+		  "current_log.free_data_pages %u, "
+		  "sequence_id %d\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  log_strategy,
+		  pebi->current_log.start_page,
+		  *cur_page, *write_offset,
+		  pebi->current_log.free_data_pages,
+		  atomic_read(&pebi->current_log.sequence_id));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	pages_per_peb = min_t(u32, fsi->leb_pages_capacity,
+				   fsi->peb_pages_capacity);
+
+	switch (log_strategy) {
+	case SSDFS_START_PARTIAL_LOG:
+	case SSDFS_CONTINUE_PARTIAL_LOG:
+		pebi->current_log.start_page = *cur_page;
+		rest_phys_free_pages = pebi->log_pages -
+					(*cur_page % pebi->log_pages);
+		pebi->current_log.free_data_pages = rest_phys_free_pages;
+		atomic_inc(&pebi->current_log.sequence_id);
+		WARN_ON(pebi->current_log.free_data_pages == 0);
+		break;
+
+	case SSDFS_FINISH_PARTIAL_LOG:
+	case SSDFS_FINISH_FULL_LOG:
+		if (*cur_page % pebi->log_pages) {
+			*cur_page += pebi->log_pages - 1;
+			*cur_page =
+			    (*cur_page / pebi->log_pages) * pebi->log_pages;
+		}
+
+		pebi->current_log.start_page = *cur_page;
+
+		if (pebi->current_log.start_page >= pages_per_peb) {
+			pebi->current_log.free_data_pages = 0;
+		} else {
+			pages_diff = pages_per_peb;
+			pages_diff -= pebi->current_log.start_page;
+
+			pebi->current_log.free_data_pages =
+				min_t(u16, pebi->log_pages, pages_diff);
+		}
+
+		atomic_set(&pebi->current_log.sequence_id, 0);
+		break;
+
+	default:
+		BUG();
+		break;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("pebi->current_log.start_page %u, "
+		  "current_log.free_data_pages %u, "
+		  "sequence_id %d\n",
+		  pebi->current_log.start_page,
+		  pebi->current_log.free_data_pages,
+		  atomic_read(&pebi->current_log.sequence_id));
+#endif /* CONFIG_SSDFS_DEBUG */
+}
+
+/*
+ * ssdfs_peb_store_pl_header_like_footer() - store partial log's header
+ * @pebi: pointer on PEB object
+ * @flags: partial log header's flags
+ * @hdr_desc: partial log header's metadata descriptor in segment header
+ * @plh_desc: partial log header's metadata descriptors array
+ * @array_size: count of items in array
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store the partial log's header
+ * in the end of the log (instead of footer).
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code.
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_pl_header_like_footer(struct ssdfs_peb_info *pebi,
+				    u32 flags,
+				    struct ssdfs_metadata_descriptor *hdr_desc,
+				    struct ssdfs_metadata_descriptor *plh_desc,
+				    size_t array_size,
+				    pgoff_t *cur_page,
+				    u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_partial_log_header *pl_hdr;
+	u32 log_pages;
+	struct page *page;
+	size_t desc_size = sizeof(struct ssdfs_metadata_descriptor);
+	size_t array_bytes = desc_size * array_size;
+	u32 area_offset, area_size;
+	u16 seg_type;
+	int sequence_id;
+	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(!hdr_desc || !plh_desc);
+	BUG_ON(!cur_page || !write_offset);
+	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, "
+		  "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;
+	seg_type = pebi->pebc->parent_si->seg_type;
+
+	sequence_id = atomic_read(&pebi->current_log.sequence_id);
+	if (sequence_id < 0 || sequence_id >= INT_MAX) {
+		SSDFS_ERR("invalid sequence_id %d\n", sequence_id);
+		return -ERANGE;
+	}
+
+	area_offset = *write_offset;
+	area_size = sizeof(struct ssdfs_partial_log_header);
+
+	*write_offset += max_t(u32, PAGE_SIZE, area_size);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(((*write_offset + PAGE_SIZE - 1) >> fsi->log_pagesize) >
+		pebi->log_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	log_pages = (*write_offset + fsi->pagesize - 1) / fsi->pagesize;
+
+	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;
+	}
+
+	pl_hdr = kmap_local_page(page);
+	memset(pl_hdr, 0xFF, PAGE_SIZE);
+	ssdfs_memcpy(pl_hdr->desc_array, 0, array_bytes,
+		     plh_desc, 0, array_bytes,
+		     array_bytes);
+
+	pl_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_partial_log_header_for_commit(fsi,
+							  sequence_id,
+							  log_pages,
+							  seg_type, flags,
+							  last_log_time,
+							  last_log_cno,
+							  pl_hdr);
+
+	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),
+			     &pl_hdr->check,
+			     0, sizeof(struct ssdfs_metadata_check),
+			     sizeof(struct ssdfs_metadata_check));
+	}
+
+	flush_dcache_page(page);
+	kunmap_local(pl_hdr);
+
+	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 partial log 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;
+	}
+
+	pebi->current_log.seg_flags |=
+		SSDFS_LOG_IS_PARTIAL |
+		SSDFS_LOG_HAS_PARTIAL_HEADER |
+		SSDFS_PARTIAL_HEADER_INSTEAD_FOOTER;
+
+	(*cur_page)++;
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_store_pl_header_like_header() - store partial log's header
+ * @pebi: pointer on PEB object
+ * @flags: partial log header's flags
+ * @plh_desc: partial log header's metadata descriptors array
+ * @array_size: count of items in array
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store the partial log's header
+ * in the beginning of the log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code.
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_pl_header_like_header(struct ssdfs_peb_info *pebi,
+				    u32 flags,
+				    struct ssdfs_metadata_descriptor *plh_desc,
+				    size_t array_size,
+				    pgoff_t *cur_page,
+				    u32 *write_offset)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_partial_log_header *pl_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;
+	int sequence_id;
+	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(!plh_desc);
+	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(*write_offset % fsi->pagesize);
+	BUG_ON((*write_offset >> fsi->log_pagesize) > pebi->log_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	sequence_id = atomic_read(&pebi->current_log.sequence_id);
+	if (sequence_id < 0 || sequence_id >= INT_MAX) {
+		SSDFS_ERR("invalid sequence_id %d\n", sequence_id);
+		return -ERANGE;
+	}
+
+	seg_type = pebi->pebc->parent_si->seg_type;
+	seg_flags = pebi->current_log.seg_flags;
+
+	log_pages = (*write_offset + fsi->pagesize - 1) / fsi->pagesize;
+
+	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;
+	}
+
+	pl_hdr = kmap_local_page(page);
+
+	ssdfs_memcpy(pl_hdr->desc_array, 0, array_bytes,
+		     plh_desc, 0, array_bytes,
+		     array_bytes);
+
+	pl_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_partial_log_header_for_commit(fsi,
+							  sequence_id,
+							  log_pages,
+							  seg_type,
+							  flags | seg_flags,
+							  last_log_time,
+							  last_log_cno,
+							  pl_hdr);
+	if (unlikely(err))
+		goto finish_pl_header_preparation;
+
+finish_pl_header_preparation:
+	flush_dcache_page(page);
+	kunmap_local(pl_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 partial log 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_store_partial_log_header() - store partial log's header
+ * @pebi: pointer on PEB object
+ * @flags: partial log header's flags
+ * @hdr_desc: partial log header's metadata descriptor in segment header
+ * @plh_desc: partial log header's metadata descriptors array
+ * @array_size: count of items in array
+ * @cur_page: pointer on current page value [in|out]
+ * @write_offset: pointer on write offset value [in|out]
+ *
+ * This function tries to store the partial log's header.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code.
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_store_partial_log_header(struct ssdfs_peb_info *pebi, u32 flags,
+				    struct ssdfs_metadata_descriptor *hdr_desc,
+				    struct ssdfs_metadata_descriptor *plh_desc,
+				    size_t array_size,
+				    pgoff_t *cur_page,
+				    u32 *write_offset)
+{
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebi || !pebi->pebc);
+	BUG_ON(!pebi->pebc->parent_si || !pebi->pebc->parent_si->fsi);
+	BUG_ON(!plh_desc);
+	BUG_ON(!cur_page || !write_offset);
+	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, "
+		  "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 */
+
+	if (hdr_desc) {
+		return ssdfs_peb_store_pl_header_like_footer(pebi, flags,
+							     hdr_desc,
+							     plh_desc,
+							     array_size,
+							     cur_page,
+							     write_offset);
+	} else {
+		return ssdfs_peb_store_pl_header_like_header(pebi, flags,
+							     plh_desc,
+							     array_size,
+							     cur_page,
+							     write_offset);
+	}
+}
+
+/*
+ * ssdfs_peb_commit_first_partial_log() - commit first partial log
+ * @pebi: pointer on PEB object
+ *
+ * This function tries to commit the first partial log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code.
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_commit_first_partial_log(struct ssdfs_peb_info *pebi)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_metadata_descriptor hdr_desc[SSDFS_SEG_HDR_DESC_MAX];
+	struct ssdfs_metadata_descriptor plh_desc[SSDFS_SEG_HDR_DESC_MAX];
+	struct ssdfs_metadata_descriptor *cur_hdr_desc;
+	u32 flags;
+	size_t desc_size = sizeof(struct ssdfs_metadata_descriptor);
+	pgoff_t cur_page = pebi->current_log.start_page;
+	u32 write_offset = 0;
+	bool log_has_data = false;
+	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(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	memset(hdr_desc, 0, desc_size * SSDFS_SEG_HDR_DESC_MAX);
+	memset(plh_desc, 0, desc_size * SSDFS_SEG_HDR_DESC_MAX);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0001: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_reserve_segment_header(pebi, &cur_page, &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to reserve segment header: "
+			   "seg %llu, peb %llu, err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id, err);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0002: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_commit_log_payload(pebi, hdr_desc, &log_has_data,
+					   &cur_page, &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to commit payload: "
+			   "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_log;
+	}
+
+	if (!log_has_data) {
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("current log hasn't data: start_page %u\n",
+			  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0003: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	cur_hdr_desc = &hdr_desc[SSDFS_LOG_FOOTER_INDEX];
+	flags = SSDFS_LOG_IS_PARTIAL |
+		SSDFS_LOG_HAS_PARTIAL_HEADER |
+		SSDFS_PARTIAL_HEADER_INSTEAD_FOOTER;
+	err = ssdfs_peb_store_partial_log_header(pebi, flags, cur_hdr_desc,
+						 plh_desc,
+						 SSDFS_SEG_HDR_DESC_MAX,
+						 &cur_page,
+						 &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to store log's partial header: "
+			   "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_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0004: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_store_log_header(pebi, hdr_desc,
+					 SSDFS_SEG_HDR_DESC_MAX,
+					 write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to store log's header: "
+			   "seg %llu, peb %llu, write_offset %u,"
+			   "err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id,
+			   write_offset, err);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0005: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_flush_current_log_dirty_pages(pebi, write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to flush current log: "
+			   "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);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0006: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	ssdfs_peb_define_next_log_start(pebi, SSDFS_START_PARTIAL_LOG,
+					&cur_page, &write_offset);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0007: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	pebi->current_log.reserved_pages = 0;
+	pebi->current_log.seg_flags = 0;
+
+	ssdfs_peb_set_current_log_state(pebi, SSDFS_LOG_COMMITTED);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("log commited: seg %llu, peb %llu\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+finish_commit_log:
+	return err;
+}
+
+/*
+ * ssdfs_peb_commit_next_partial_log() - commit next partial log
+ * @pebi: pointer on PEB object
+ *
+ * This function tries to commit the next partial log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code.
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_commit_next_partial_log(struct ssdfs_peb_info *pebi)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_metadata_descriptor plh_desc[SSDFS_SEG_HDR_DESC_MAX];
+	u32 flags;
+	size_t desc_size = sizeof(struct ssdfs_metadata_descriptor);
+	pgoff_t cur_page = pebi->current_log.start_page;
+	u32 write_offset = 0;
+	bool log_has_data = false;
+	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(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	memset(plh_desc, 0, desc_size * SSDFS_SEG_HDR_DESC_MAX);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0001: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_reserve_partial_log_header(pebi, &cur_page, &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to reserve partial log's header: "
+			   "seg %llu, peb %llu, err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id, err);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0002: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_commit_log_payload(pebi, plh_desc, &log_has_data,
+					   &cur_page, &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to commit payload: "
+			   "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_log;
+	}
+
+	if (!log_has_data) {
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("current log hasn't data: start_page %u\n",
+			  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+	}
+
+	flags = SSDFS_LOG_IS_PARTIAL |
+		SSDFS_LOG_HAS_PARTIAL_HEADER;
+	err = ssdfs_peb_store_partial_log_header(pebi, flags, NULL,
+						 plh_desc,
+						 SSDFS_SEG_HDR_DESC_MAX,
+						 &cur_page,
+						 &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to store log's partial header: "
+			   "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_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0003: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_flush_current_log_dirty_pages(pebi, write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to flush current log: "
+			   "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);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0004: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	ssdfs_peb_define_next_log_start(pebi, SSDFS_CONTINUE_PARTIAL_LOG,
+					&cur_page, &write_offset);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0005: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	pebi->current_log.reserved_pages = 0;
+	pebi->current_log.seg_flags = 0;
+
+	ssdfs_peb_set_current_log_state(pebi, SSDFS_LOG_COMMITTED);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("log commited: seg %llu, peb %llu\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+finish_commit_log:
+	return err;
+}
+
+/*
+ * ssdfs_peb_commit_last_partial_log() - commit last partial log
+ * @pebi: pointer on PEB object
+ * @cur_segs: current segment IDs array
+ * @cur_segs_size: size of segment IDs array size in bytes
+ *
+ * This function tries to commit the last partial log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code.
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_commit_last_partial_log(struct ssdfs_peb_info *pebi,
+					__le64 *cur_segs,
+					size_t cur_segs_size)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_metadata_descriptor plh_desc[SSDFS_SEG_HDR_DESC_MAX];
+	struct ssdfs_metadata_descriptor lf_desc[SSDFS_LOG_FOOTER_DESC_MAX];
+	struct ssdfs_metadata_descriptor *cur_hdr_desc;
+	u32 flags;
+	size_t desc_size = sizeof(struct ssdfs_metadata_descriptor);
+	pgoff_t cur_page = pebi->current_log.start_page;
+	pgoff_t cur_page_offset;
+	u32 write_offset = 0;
+	bool log_has_data = false;
+	int log_strategy = SSDFS_FINISH_PARTIAL_LOG;
+	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(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	memset(plh_desc, 0, desc_size * SSDFS_SEG_HDR_DESC_MAX);
+	memset(lf_desc, 0, desc_size * SSDFS_LOG_FOOTER_DESC_MAX);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0001: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_reserve_partial_log_header(pebi, &cur_page, &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to reserve partial log's header: "
+			   "seg %llu, peb %llu, err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id, err);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0002: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_commit_log_payload(pebi, plh_desc, &log_has_data,
+					   &cur_page, &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to commit payload: "
+			   "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_log;
+	}
+
+	if (!log_has_data) {
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("current log hasn't data: start_page %u\n",
+			  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0003: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	cur_page_offset = cur_page % pebi->log_pages;
+
+	if (cur_page_offset == 0) {
+		/*
+		 * There is no space for log footer.
+		 * So, full log will be without footer.
+		 */
+		SSDFS_DBG("There is no space for log footer.\n");
+
+		flags = SSDFS_LOG_IS_PARTIAL |
+			SSDFS_LOG_HAS_PARTIAL_HEADER;
+		log_strategy = SSDFS_FINISH_PARTIAL_LOG;
+	} else if ((pebi->log_pages - cur_page_offset) == 1) {
+		cur_hdr_desc = &plh_desc[SSDFS_LOG_FOOTER_INDEX];
+		flags = SSDFS_PARTIAL_LOG_FOOTER | SSDFS_ENDING_LOG_FOOTER;
+		err = ssdfs_peb_store_log_footer(pebi, flags, cur_hdr_desc,
+						 lf_desc,
+						 SSDFS_LOG_FOOTER_DESC_MAX,
+						 cur_segs, cur_segs_size,
+						 &cur_page,
+						 &write_offset);
+		if (unlikely(err)) {
+			SSDFS_CRIT("fail to store log's footer: "
+				   "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_log;
+		}
+
+		flags = SSDFS_LOG_IS_PARTIAL |
+			SSDFS_LOG_HAS_PARTIAL_HEADER |
+			SSDFS_LOG_HAS_FOOTER;
+		log_strategy = SSDFS_FINISH_PARTIAL_LOG;
+	} else {
+		/*
+		 * It is possible to add another log.
+		 */
+		flags = SSDFS_LOG_IS_PARTIAL |
+			SSDFS_LOG_HAS_PARTIAL_HEADER;
+		log_strategy = SSDFS_CONTINUE_PARTIAL_LOG;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0004: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_store_partial_log_header(pebi, flags, NULL,
+						 plh_desc,
+						 SSDFS_SEG_HDR_DESC_MAX,
+						 &cur_page,
+						 &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to store log's partial header: "
+			   "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_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0005: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_flush_current_log_dirty_pages(pebi, write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to flush current log: "
+			   "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);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0006: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	ssdfs_peb_define_next_log_start(pebi, log_strategy,
+					&cur_page, &write_offset);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0007: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	pebi->current_log.reserved_pages = 0;
+	pebi->current_log.seg_flags = 0;
+
+	ssdfs_peb_set_current_log_state(pebi, SSDFS_LOG_COMMITTED);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("log commited: seg %llu, peb %llu\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+finish_commit_log:
+	return err;
+}
+
+/*
+ * ssdfs_peb_commit_full_log() - commit full current log
+ * @pebi: pointer on PEB object
+ * @cur_segs: current segment IDs array
+ * @cur_segs_size: size of segment IDs array size in bytes
+ *
+ * This function tries to commit the current log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code.
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_commit_full_log(struct ssdfs_peb_info *pebi,
+				__le64 *cur_segs,
+				size_t cur_segs_size)
+{
+	struct ssdfs_fs_info *fsi;
+	struct ssdfs_metadata_descriptor hdr_desc[SSDFS_SEG_HDR_DESC_MAX];
+	struct ssdfs_metadata_descriptor plh_desc[SSDFS_SEG_HDR_DESC_MAX];
+	struct ssdfs_metadata_descriptor lf_desc[SSDFS_LOG_FOOTER_DESC_MAX];
+	struct ssdfs_metadata_descriptor *cur_hdr_desc;
+	int log_strategy = SSDFS_FINISH_FULL_LOG;
+	u32 flags;
+	size_t desc_size = sizeof(struct ssdfs_metadata_descriptor);
+	pgoff_t cur_page = pebi->current_log.start_page;
+	pgoff_t cur_page_offset;
+	u32 write_offset = 0;
+	bool log_has_data = false;
+	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(!is_ssdfs_peb_current_log_locked(pebi));
+
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	fsi = pebi->pebc->parent_si->fsi;
+
+	memset(hdr_desc, 0, desc_size * SSDFS_SEG_HDR_DESC_MAX);
+	memset(lf_desc, 0, desc_size * SSDFS_LOG_FOOTER_DESC_MAX);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0001: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_reserve_segment_header(pebi, &cur_page, &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to reserve segment header: "
+			   "seg %llu, peb %llu, err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id, err);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0002: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_commit_log_payload(pebi, hdr_desc, &log_has_data,
+					   &cur_page, &write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to commit payload: "
+			   "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_log;
+	}
+
+	if (!log_has_data) {
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("current log hasn't data: start_page %u\n",
+			  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_DEBUG */
+		goto define_next_log_start;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0003: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	cur_page_offset = cur_page % pebi->log_pages;
+	if (cur_page_offset == 0) {
+		SSDFS_WARN("There is no space for log footer.\n");
+	}
+
+	if ((pebi->log_pages - cur_page_offset) > 1) {
+		log_strategy = SSDFS_START_PARTIAL_LOG;
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("start partial log: "
+			  "cur_page_offset %lu, pebi->log_pages %u\n",
+			  cur_page_offset, pebi->log_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		cur_hdr_desc = &hdr_desc[SSDFS_LOG_FOOTER_INDEX];
+		flags = SSDFS_LOG_IS_PARTIAL |
+			SSDFS_LOG_HAS_PARTIAL_HEADER |
+			SSDFS_PARTIAL_HEADER_INSTEAD_FOOTER;
+		err = ssdfs_peb_store_partial_log_header(pebi, flags,
+							 cur_hdr_desc,
+							 plh_desc,
+							 SSDFS_SEG_HDR_DESC_MAX,
+							 &cur_page,
+							 &write_offset);
+		if (unlikely(err)) {
+			SSDFS_CRIT("fail to store log's partial header: "
+				   "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_log;
+		}
+	} else {
+		log_strategy = SSDFS_FINISH_FULL_LOG;
+
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_DBG("finish full log: "
+			  "cur_page_offset %lu, pebi->log_pages %u\n",
+			  cur_page_offset, pebi->log_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		cur_hdr_desc = &hdr_desc[SSDFS_LOG_FOOTER_INDEX];
+		flags = 0;
+		err = ssdfs_peb_store_log_footer(pebi, flags, cur_hdr_desc,
+						 lf_desc,
+						 SSDFS_LOG_FOOTER_DESC_MAX,
+						 cur_segs, cur_segs_size,
+						 &cur_page,
+						 &write_offset);
+		if (unlikely(err)) {
+			SSDFS_CRIT("fail to store log's footer: "
+				   "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_log;
+		}
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0004: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_store_log_header(pebi, hdr_desc,
+					 SSDFS_SEG_HDR_DESC_MAX,
+					 write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to store log's header: "
+			   "seg %llu, peb %llu, write_offset %u,"
+			   "err %d\n",
+			   pebi->pebc->parent_si->seg_id, pebi->peb_id,
+			   write_offset, err);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0005: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	err = ssdfs_peb_flush_current_log_dirty_pages(pebi, write_offset);
+	if (unlikely(err)) {
+		SSDFS_CRIT("fail to flush current log: "
+			   "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);
+		goto finish_commit_log;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0006: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+define_next_log_start:
+	ssdfs_peb_define_next_log_start(pebi, log_strategy,
+					&cur_page, &write_offset);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("0007: cur_page %lu, write_offset %u\n",
+		  cur_page, write_offset);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	pebi->current_log.reserved_pages = 0;
+	pebi->current_log.seg_flags = 0;
+
+	ssdfs_peb_set_current_log_state(pebi, SSDFS_LOG_COMMITTED);
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("log commited: seg %llu, peb %llu\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+finish_commit_log:
+	return err;
+}
+
+/*
+ * ssdfs_peb_calculate_reserved_metapages() - calculate reserved metapages
+ * @page_size: size of page in bytes
+ * @data_pages: number of allocated data pages
+ * @pebs_per_seg: number of PEBs in one segment
+ * @log_strategy: stategy of log commiting
+ */
+u16 ssdfs_peb_calculate_reserved_metapages(u32 page_size,
+					   u32 data_pages,
+					   u32 pebs_per_seg,
+					   int log_strategy)
+{
+	size_t seg_hdr_size = sizeof(struct ssdfs_segment_header);
+	size_t lf_hdr_size = sizeof(struct ssdfs_log_footer);
+	u32 blk_bmap_bytes = 0;
+	u32 blk2off_tbl_bytes = 0;
+	u32 blk_desc_tbl_bytes = 0;
+	u32 reserved_bytes = 0;
+	u32 reserved_pages = 0;
+
+	/* segment header */
+	reserved_bytes += seg_hdr_size;
+
+	/* block bitmap */
+	blk_bmap_bytes = __ssdfs_peb_estimate_blk_bmap_bytes(data_pages, true);
+	reserved_bytes += blk_bmap_bytes;
+
+	/* blk2off table */
+	blk2off_tbl_bytes = __ssdfs_peb_estimate_blk2off_bytes(data_pages,
+								pebs_per_seg);
+	reserved_bytes += blk2off_tbl_bytes;
+
+	/* block descriptor table */
+	blk_desc_tbl_bytes =
+		__ssdfs_peb_estimate_blk_desc_tbl_bytes(data_pages);
+	reserved_bytes += blk_desc_tbl_bytes;
+
+	reserved_bytes += page_size - 1;
+	reserved_bytes /= page_size;
+	reserved_bytes *= page_size;
+
+	switch (log_strategy) {
+	case SSDFS_START_FULL_LOG:
+	case SSDFS_FINISH_PARTIAL_LOG:
+	case SSDFS_FINISH_FULL_LOG:
+		/* log footer header */
+		reserved_bytes += lf_hdr_size;
+
+		/* block bitmap */
+		reserved_bytes += blk_bmap_bytes;
+
+		/* blk2off table */
+		reserved_bytes += blk2off_tbl_bytes;
+
+		reserved_bytes += page_size - 1;
+		reserved_bytes /= page_size;
+		reserved_bytes *= page_size;
+
+		reserved_pages = reserved_bytes / page_size;
+		break;
+
+	case SSDFS_START_PARTIAL_LOG:
+	case SSDFS_CONTINUE_PARTIAL_LOG:
+		/* do nothing */
+		break;
+
+	default:
+		SSDFS_CRIT("unexpected log strategy %#x\n",
+			   log_strategy);
+		return U16_MAX;
+	}
+
+	reserved_pages = reserved_bytes / page_size;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("data_pages %u, log_strategy %#x, "
+		  "blk_bmap_bytes %u, blk2off_tbl_bytes %u, "
+		  "blk_desc_tbl_bytes %u, reserved_bytes %u, "
+		  "reserved_pages %u\n",
+		  data_pages, log_strategy,
+		  blk_bmap_bytes, blk2off_tbl_bytes,
+		  blk_desc_tbl_bytes, reserved_bytes,
+		  reserved_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	BUG_ON(reserved_pages >= U16_MAX);
+
+	return (u16)reserved_pages;
+}
+
+/*
+ * ssdfs_peb_commit_log() - commit current log
+ * @pebi: pointer on PEB object
+ * @cur_segs: current segment IDs array
+ * @cur_segs_size: size of segment IDs array size in bytes
+ *
+ * This function tries to commit the current log.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code.
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - internal error.
+ */
+static
+int ssdfs_peb_commit_log(struct ssdfs_peb_info *pebi,
+			 __le64 *cur_segs, size_t cur_segs_size)
+{
+	struct ssdfs_segment_info *si;
+	struct ssdfs_blk2off_table *table;
+	int log_state;
+	int log_strategy;
+	u32 page_size;
+	u32 pebs_per_seg;
+	u32 pages_per_peb;
+	int used_pages;
+	int invalid_pages;
+	u32 data_pages;
+	u16 reserved_pages;
+	u16 diff;
+	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(!is_ssdfs_peb_current_log_locked(pebi));
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	log_state = atomic_read(&pebi->current_log.state);
+
+	switch (log_state) {
+	case SSDFS_LOG_UNKNOWN:
+	case SSDFS_LOG_PREPARED:
+	case SSDFS_LOG_INITIALIZED:
+		SSDFS_WARN("peb %llu current log can't be commited\n",
+			   pebi->peb_id);
+		return -EINVAL;
+
+	case SSDFS_LOG_CREATED:
+		/* do function's work */
+		break;
+
+	case SSDFS_LOG_COMMITTED:
+		SSDFS_WARN("peb %llu current log has been commited\n",
+			   pebi->peb_id);
+		return 0;
+
+	default:
+		BUG();
+	};
+
+#ifdef CONFIG_SSDFS_TRACK_API_CALL
+	SSDFS_ERR("seg %llu, peb %llu, current_log.start_page %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page);
+#else
+	SSDFS_DBG("seg %llu, peb %llu, current_log.start_page %u\n",
+		  pebi->pebc->parent_si->seg_id, pebi->peb_id,
+		  pebi->current_log.start_page);
+#endif /* CONFIG_SSDFS_TRACK_API_CALL */
+
+	si = pebi->pebc->parent_si;
+	log_strategy = is_log_partial(pebi);
+	page_size = pebi->pebc->parent_si->fsi->pagesize;
+	pebs_per_seg = pebi->pebc->parent_si->fsi->pebs_per_seg;
+	pages_per_peb = pebi->pebc->parent_si->fsi->pages_per_peb;
+
+	used_pages = ssdfs_peb_get_used_data_pages(pebi->pebc);
+	if (used_pages < 0) {
+		err = used_pages;
+		SSDFS_ERR("fail to get used data pages count: "
+			  "seg %llu, peb %llu, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id, err);
+		return err;
+	}
+
+	invalid_pages = ssdfs_peb_get_invalid_pages(pebi->pebc);
+	if (invalid_pages < 0) {
+		err = invalid_pages;
+		SSDFS_ERR("fail to get invalid pages count: "
+			  "seg %llu, peb %llu, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id, err);
+		return err;
+	}
+
+	data_pages = used_pages + invalid_pages;
+
+	if (data_pages == 0) {
+		SSDFS_ERR("invalid data pages count: "
+			  "used_pages %d, invalid_pages %d, "
+			  "data_pages %u\n",
+			  used_pages, invalid_pages, data_pages);
+		return -ERANGE;
+	}
+
+	reserved_pages = ssdfs_peb_calculate_reserved_metapages(page_size,
+								data_pages,
+								pebs_per_seg,
+								log_strategy);
+	if (reserved_pages > pages_per_peb) {
+		SSDFS_ERR("reserved_pages %u > pages_per_peb %u\n",
+			  reserved_pages, pages_per_peb);
+		return -ERANGE;
+	}
+
+	if (reserved_pages > pebi->current_log.reserved_pages) {
+		diff = reserved_pages - pebi->current_log.reserved_pages;
+
+		err = ssdfs_segment_blk_bmap_reserve_metapages(&si->blk_bmap,
+								pebi->pebc,
+								diff);
+		if (err == -ENOSPC) {
+			/* ignore error */
+			err = 0;
+		} else if (unlikely(err)) {
+			SSDFS_ERR("fail to reserve metadata pages: "
+				  "count %u, err %d\n",
+				  diff, err);
+			return err;
+		}
+
+		pebi->current_log.reserved_pages += diff;
+		if (diff > pebi->current_log.free_data_pages)
+			pebi->current_log.free_data_pages = 0;
+		else
+			pebi->current_log.free_data_pages -= diff;
+	} else if (reserved_pages < pebi->current_log.reserved_pages) {
+		diff = pebi->current_log.reserved_pages - reserved_pages;
+
+		err = ssdfs_segment_blk_bmap_free_metapages(&si->blk_bmap,
+							    pebi->pebc,
+							    diff);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to free metadata pages: "
+				  "count %u, err %d\n",
+				  diff, err);
+			return err;
+		}
+
+		pebi->current_log.reserved_pages -= diff;
+		pebi->current_log.free_data_pages += diff;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("data_pages %u, "
+		  "current_log (reserved_pages %u, free_data_pages %u)\n",
+		  data_pages,
+		  pebi->current_log.reserved_pages,
+		  pebi->current_log.free_data_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	pebi->current_log.last_log_time = ssdfs_current_timestamp();
+	pebi->current_log.last_log_cno = ssdfs_current_cno(si->fsi->sb);
+
+	log_strategy = is_log_partial(pebi);
+
+	switch (log_strategy) {
+	case SSDFS_START_FULL_LOG:
+		SSDFS_CRIT("log contains nothing: "
+			   "seg %llu, peb %llu, "
+			   "free_data_pages %u\n",
+			   pebi->pebc->parent_si->seg_id,
+			   pebi->peb_id,
+			   pebi->current_log.free_data_pages);
+		return -ERANGE;
+
+	case SSDFS_START_PARTIAL_LOG:
+		err = ssdfs_peb_commit_first_partial_log(pebi);
+		if (unlikely(err)) {
+			SSDFS_CRIT("fail to commit first partial log: "
+				   "err %d\n", err);
+			return err;
+		}
+		break;
+
+	case SSDFS_CONTINUE_PARTIAL_LOG:
+		err = ssdfs_peb_commit_next_partial_log(pebi);
+		if (unlikely(err)) {
+			SSDFS_CRIT("fail to commit next partial log: "
+				   "err %d\n", err);
+			return err;
+		}
+		break;
+
+	case SSDFS_FINISH_PARTIAL_LOG:
+		err = ssdfs_peb_commit_last_partial_log(pebi, cur_segs,
+							cur_segs_size);
+		if (unlikely(err)) {
+			SSDFS_CRIT("fail to commit last partial log: "
+				   "err %d\n", err);
+			return err;
+		}
+		break;
+
+	case SSDFS_FINISH_FULL_LOG:
+		err = ssdfs_peb_commit_full_log(pebi, cur_segs,
+						cur_segs_size);
+		if (unlikely(err)) {
+			SSDFS_CRIT("fail to commit full log: "
+				   "err %d\n", err);
+			return err;
+		}
+		break;
+
+	default:
+		SSDFS_CRIT("unexpected log strategy %#x\n",
+			   log_strategy);
+		return -ERANGE;
+	}
+
+	table = pebi->pebc->parent_si->blk2off_table;
+
+	err = ssdfs_blk2off_table_revert_migration_state(table,
+							 pebi->peb_index);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to revert migration state: "
+			  "seg %llu, peb %llu, peb_index %u, err %d\n",
+			  pebi->pebc->parent_si->seg_id,
+			  pebi->peb_id,
+			  pebi->peb_index,
+			  err);
+	}
+
+#ifdef CONFIG_SSDFS_TRACK_API_CALL
+	SSDFS_ERR("finished\n");
+#else
+	SSDFS_DBG("finished\n");
+#endif /* CONFIG_SSDFS_TRACK_API_CALL */
+
+	return err;
+}
+
+/*
+ * ssdfs_peb_remain_log_creation_thread() - remain as log creation thread
+ * @pebc: pointer on PEB container
+ *
+ * This function check that PEB's flush thread can work
+ * as thread that creates logs.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-ENOSPC     - PEB hasn't free space.
+ */
+static
+int ssdfs_peb_remain_log_creation_thread(struct ssdfs_peb_container *pebc)
+{
+	struct ssdfs_segment_info *si;
+	int peb_free_pages;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebc || !pebc->parent_si);
+
+	SSDFS_DBG("seg %llu, peb_index %d\n",
+		  pebc->parent_si->seg_id, pebc->peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	si = pebc->parent_si;
+
+	peb_free_pages = ssdfs_peb_get_free_pages(pebc);
+	if (unlikely(peb_free_pages < 0)) {
+		err = peb_free_pages;
+		SSDFS_ERR("fail to calculate PEB's free pages: "
+			  "seg %llu, peb index %d, err %d\n",
+			  pebc->parent_si->seg_id,
+			  pebc->peb_index, err);
+		return err;
+	}
+
+#ifdef CONFIG_SSDFS_DEBUG
+	SSDFS_DBG("peb_free_pages %d\n", peb_free_pages);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	if (peb_free_pages == 0) {
+		SSDFS_DBG("PEB hasn't free space\n");
+		return -ENOSPC;
+	}
+
+	if (!is_peb_joined_into_create_requests_queue(pebc)) {
+#ifdef CONFIG_SSDFS_DEBUG
+		SSDFS_WARN("peb_index %u hasn't creation role\n",
+			    pebc->peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+		err = ssdfs_peb_join_create_requests_queue(pebc,
+							   &si->create_rq);
+		if (unlikely(err)) {
+			SSDFS_ERR("fail to join create requests queue: "
+				  "seg %llu, peb_index %d, err %d\n",
+				  si->seg_id, pebc->peb_index, err);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_delegate_log_creation_role() - try to delegate log creation role
+ * @pebc: pointer on PEB container
+ * @found_peb_index: index of PEB candidate
+ *
+ * This function tries to delegate the role of logs creation to
+ * PEB with @found_peb_index.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-EAGAIN     - it needs to search another candidate.
+ */
+static
+int ssdfs_peb_delegate_log_creation_role(struct ssdfs_peb_container *pebc,
+					 int found_peb_index)
+{
+	struct ssdfs_segment_info *si;
+	struct ssdfs_peb_container *found_pebc;
+	int peb_free_pages;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebc || !pebc->parent_si);
+	BUG_ON(found_peb_index >= pebc->parent_si->pebs_count);
+
+	SSDFS_DBG("seg %llu, peb_index %d, found_peb_index %d\n",
+		  pebc->parent_si->seg_id, pebc->peb_index,
+		  found_peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	si = pebc->parent_si;
+
+	if (found_peb_index == pebc->peb_index) {
+		err = ssdfs_peb_remain_log_creation_thread(pebc);
+		if (err == -ENOSPC) {
+			SSDFS_WARN("PEB hasn't free space\n");
+			return err;
+		} else if (unlikely(err)) {
+			SSDFS_ERR("fail to remain log creation thread: "
+				  "seg %llu, peb_index %d, "
+				  "err %d\n",
+				  si->seg_id, pebc->peb_index, err);
+			return err;
+		}
+
+		return 0;
+	}
+
+	found_pebc = &si->peb_array[found_peb_index];
+
+	peb_free_pages = ssdfs_peb_get_free_pages(found_pebc);
+	if (unlikely(peb_free_pages < 0)) {
+		err = peb_free_pages;
+		SSDFS_ERR("fail to calculate PEB's free pages: "
+			  "seg %llu, peb index %d, err %d\n",
+			  found_pebc->parent_si->seg_id,
+			  found_pebc->peb_index, err);
+		return err;
+	}
+
+	if (peb_free_pages == 0)
+		return -EAGAIN;
+
+	if (is_peb_joined_into_create_requests_queue(found_pebc)) {
+		SSDFS_WARN("PEB is creating log: "
+			   "seg %llu, peb_index %d\n",
+			   found_pebc->parent_si->seg_id,
+			   found_pebc->peb_index);
+		return -EAGAIN;
+	}
+
+	ssdfs_peb_forget_create_requests_queue(pebc);
+
+	err = ssdfs_peb_join_create_requests_queue(found_pebc,
+						   &si->create_rq);
+	if (unlikely(err)) {
+		SSDFS_ERR("fail to join create requests queue: "
+			  "seg %llu, peb_index %d, err %d\n",
+			  si->seg_id, found_pebc->peb_index, err);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * ssdfs_peb_find_next_log_creation_thread() - search PEB for logs creation
+ * @pebc: pointer on PEB container
+ *
+ * This function tries to find and to delegate the role of logs creation
+ * to another PEB's flush thread.
+ *
+ * RETURN:
+ * [success]
+ * [failure] - error code:
+ *
+ * %-EINVAL     - invalid input.
+ * %-ERANGE     - fail to find another PEB.
+ */
+static
+int ssdfs_peb_find_next_log_creation_thread(struct ssdfs_peb_container *pebc)
+{
+	struct ssdfs_segment_info *si;
+	int start_pos;
+	int i;
+	int err;
+
+#ifdef CONFIG_SSDFS_DEBUG
+	BUG_ON(!pebc || !pebc->parent_si);
+
+	SSDFS_DBG("seg %llu, peb_index %d\n",
+		  pebc->parent_si->seg_id, pebc->peb_index);
+#endif /* CONFIG_SSDFS_DEBUG */
+
+	si = pebc->parent_si;
+
+	if (!is_peb_joined_into_create_requests_queue(pebc)) {
+		SSDFS_WARN("peb_index %u hasn't creation role\n",
+			    pebc->peb_index);
+		return -EINVAL;
+	}
+
+	start_pos = pebc->peb_index + si->create_threads;
+
+	if (start_pos >= si->pebs_count)
+		start_pos = pebc->peb_index % si->create_threads;
+
+	if (start_pos == pebc->peb_index) {
+		err = ssdfs_peb_remain_log_creation_thread(pebc);
+		if (err == -ENOSPC) {
+			SSDFS_DBG("PEB hasn't free space\n");
+			return 0;
+		} else if (unlikely(err)) {
+			SSDFS_ERR("fail to remain log creation thread: "
+				  "seg %llu, peb_index %d, "
+				  "err %d\n",
+				  si->seg_id, pebc->peb_index, err);
+			return err;
+		} else
+			return 0;
+	}
+
+	if (start_pos < pebc->peb_index)
+		goto search_from_begin;
+
+	for (i = start_pos; i < si->pebs_count; i += si->create_threads) {
+		err = ssdfs_peb_delegate_log_creation_role(pebc, i);
+		if (err == -EAGAIN)
+			continue;
+		else if (err == -ENOSPC) {
+			SSDFS_WARN("PEB hasn't free space\n");
+			return err;
+		} else if (unlikely(err)) {
+			SSDFS_ERR("fail to delegate log creation role: "
+				  "seg %llu, peb_index %d, "
+				  "found_peb_index %d, err %d\n",
+				  si->seg_id, pebc->peb_index,
+				  i, err);
+			return err;
+		} else
+			return 0;
+	}
+
+	start_pos = pebc->peb_index % si->create_threads;
+
+search_from_begin:
+	for (i = start_pos; i <= pebc->peb_index; i += si->create_threads) {
+		err = ssdfs_peb_delegate_log_creation_role(pebc, i);
+		if (err == -EAGAIN)
+			continue;
+		if (err == -ENOSPC) {
+			SSDFS_WARN("PEB hasn't free space\n");
+			return err;
+		} else if (unlikely(err)) {
+			SSDFS_ERR("fail to delegate log creation role: "
+				  "seg %llu, peb_index %d, "
+				  "found_peb_index %d, err %d\n",
+				  si->seg_id, pebc->peb_index,
+				  i, err);
+			return err;
+		} else
+			return 0;
+	}
+
+	SSDFS_ERR("fail to delegate log creation role: "
+		  "seg %llu, peb_index %d\n",
+		  si->seg_id, pebc->peb_index);
+	return -ERANGE;
+}
+
 /*
  * __ssdfs_finish_request() - common logic of request's finishing
  * @pebc: pointer on PEB container
