|
|
|
@ -98,6 +98,7 @@ static void reset_journal(struct ftl_journal *j) |
|
|
|
|
j->head = 0; |
|
|
|
|
j->tail = 0; |
|
|
|
|
j->root = UINT32_MAX; |
|
|
|
|
j->nused_pages = 0; |
|
|
|
|
j->epoch = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -259,8 +260,8 @@ static int find_root(struct ftl_journal *j, uint32_t group) |
|
|
|
|
|
|
|
|
|
if (read_page_desc(j, &page_desc, upage) < 0) |
|
|
|
|
return -1; |
|
|
|
|
/* TODO: better condition? */ |
|
|
|
|
} while (page_desc.va != UINT32_MAX); |
|
|
|
|
} while (page_desc.va != UINT32_MAX || |
|
|
|
|
page_desc.nused_pages == 0); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
@ -287,6 +288,9 @@ static int find_head(struct ftl_journal *j) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int read_page_group(struct ftl_journal *j, |
|
|
|
|
struct ftl_page_group *group, uint32_t group_no); |
|
|
|
|
|
|
|
|
|
/* Resumes the journal by finding the first block that is in use, the last
|
|
|
|
|
* block that is in use, the last page group that is in use, and setting the |
|
|
|
|
* head to the first free user page. |
|
|
|
@ -294,6 +298,7 @@ static int find_head(struct ftl_journal *j) |
|
|
|
|
int ftl_resume_journal(struct ftl_journal *j) |
|
|
|
|
{ |
|
|
|
|
struct ftl_page_group group; |
|
|
|
|
struct ftl_page_desc page_desc; |
|
|
|
|
uint32_t first, last, group_no; |
|
|
|
|
|
|
|
|
|
if (!j) |
|
|
|
@ -315,6 +320,15 @@ int ftl_resume_journal(struct ftl_journal *j) |
|
|
|
|
if (find_head(j) < 0) |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
if (read_page_group(j, &group, j->root >> j->log2_pages_per_group) < 0) |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
if (read_page_desc(j, &page_desc, j->root) < 0) |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
j->tail = group.tail; |
|
|
|
|
j->nused_pages = page_desc.nused_pages; |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -376,6 +390,17 @@ int read_page_desc(struct ftl_journal *j, |
|
|
|
|
return flash_read(j->dev, addr + offset, page_desc, sizeof *page_desc); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int read_page_group(struct ftl_journal *j, |
|
|
|
|
struct ftl_page_group *group, uint32_t group_no) |
|
|
|
|
{ |
|
|
|
|
uint32_t page, addr; |
|
|
|
|
|
|
|
|
|
page = ((group_no + 1) << j->log2_pages_per_group) - 1; |
|
|
|
|
addr = page << j->log2_page_size; |
|
|
|
|
|
|
|
|
|
return flash_read(j->dev, addr, group, sizeof *group); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Trace a path for a given virtual target address by comparing each of the
|
|
|
|
|
* bits in the target address with the virtual address of our root. In case of |
|
|
|
|
* a mismatch, we proceed our traversal with the given subtree at the current |
|
|
|
@ -461,6 +486,8 @@ static int free_page(struct ftl_journal *j, uint32_t upage) |
|
|
|
|
if (upage != found_upage) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
page_desc.nused_pages = j->nused_pages; |
|
|
|
|
|
|
|
|
|
if (flash_copy(j->dev, j->head << j->log2_page_size, |
|
|
|
|
upage << j->log2_page_size, 1 << j->log2_page_size) < 0) |
|
|
|
|
return -1; |
|
|
|
@ -603,10 +630,14 @@ int ftl_write(struct ftl_journal *j, uint32_t va, const uint8_t *data) |
|
|
|
|
!is_aligned(va, 1 << j->log2_page_size)) |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
if ((ret = trace_path(j, &page_desc, NULL, va)) < 0) { |
|
|
|
|
if (ret != -ERR_NOT_FOUND) |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
if ((ret = trace_path(j, &page_desc, NULL, va)) < 0 && |
|
|
|
|
ret != -ERR_NOT_FOUND) |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
if (ret == -ERR_NOT_FOUND) |
|
|
|
|
++j->nused_pages; |
|
|
|
|
|
|
|
|
|
page_desc.nused_pages = j->nused_pages; |
|
|
|
|
|
|
|
|
|
return ftl_write_upage(j, data, &page_desc); |
|
|
|
|
} |
|
|
|
@ -625,6 +656,8 @@ int ftl_trim(struct ftl_journal *j, uint32_t va) |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
--j->nused_pages; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < 32; ++i) { |
|
|
|
|
level = 32 - i - 1; |
|
|
|
|
|
|
|
|
@ -643,6 +676,7 @@ int ftl_trim(struct ftl_journal *j, uint32_t va) |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
page_desc.va = alt_page_desc.va; |
|
|
|
|
page_desc.nused_pages = j->nused_pages; |
|
|
|
|
page_desc.subtrees[level] = UINT32_MAX; |
|
|
|
|
|
|
|
|
|
for (i = level + 1; i < 32; ++i) { |
|
|
|
@ -656,6 +690,14 @@ int ftl_trim(struct ftl_journal *j, uint32_t va) |
|
|
|
|
return ftl_write_upage(j, NULL, &page_desc); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Returns the amount of used pages with a unique virtual address multiplied by
|
|
|
|
|
* the page size as the size of used space on the device. |
|
|
|
|
*/ |
|
|
|
|
uint32_t ftl_get_size(const struct ftl_journal *j) |
|
|
|
|
{ |
|
|
|
|
return j->nused_pages << j->log2_page_size; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* The capacity of the device is the total amount of user pages minus a block
|
|
|
|
|
* worth of user pages for garbage collection. |
|
|
|
|
*/ |
|
|
|
|