You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
4.4 KiB
220 lines
4.4 KiB
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <bitops.h>
|
|
#include <flash.h>
|
|
#include <ftl.h>
|
|
#include <macros.h>
|
|
|
|
#include "gc.h"
|
|
#include "map.h"
|
|
|
|
int ftl_is_mapped(struct ftl_map *map, uint32_t va)
|
|
{
|
|
if (trace_path(map, NULL, NULL, va) < 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
size_t ftl_read(struct ftl_map *map, void *data, size_t len, uint32_t va)
|
|
{
|
|
int ret;
|
|
uint32_t offset, page;
|
|
|
|
if (!data)
|
|
return 0;
|
|
|
|
if (ftl_sync(map) < 0)
|
|
return 0;
|
|
|
|
offset = va & ((1 << map->log2_page_size) - 1);
|
|
va >>= map->log2_page_size;
|
|
len = min(len, (1 << map->log2_page_size) - offset);
|
|
|
|
if ((ret = trace_path(map, NULL, &page, va)) < 0) {
|
|
memset(data, 0, len);
|
|
return len;
|
|
}
|
|
|
|
return flash_read(map->dev, (page << map->log2_page_size) + offset, data,
|
|
len);
|
|
}
|
|
|
|
static int ftl_seek(struct ftl_map *map, uint32_t va)
|
|
{
|
|
size_t len;
|
|
int ret;
|
|
uint32_t offset, page, dst, src;
|
|
|
|
if (va >= ftl_get_capacity(map))
|
|
return -1;
|
|
|
|
offset = va & ((1 << map->log2_page_size) - 1);
|
|
va >>= map->log2_page_size;
|
|
|
|
if ((map->flags & FTL_CACHED_VA) && map->last_va != va &&
|
|
ftl_sync(map) < 0)
|
|
return -1;
|
|
|
|
if ((ret = trace_path(map, NULL, &page, va)) < 0 &&
|
|
ret != -ERR_NOT_FOUND)
|
|
return -1;
|
|
|
|
if (offset < map->offset)
|
|
return -1;
|
|
|
|
if (offset == map->offset)
|
|
return 0;
|
|
|
|
if (map->offset == 0) {
|
|
if (prepare_head(map) < 0)
|
|
return -1;
|
|
|
|
if (ret == -ERR_NOT_FOUND)
|
|
++map->nused_pages;
|
|
}
|
|
|
|
dst = map->head << map->log2_page_size;
|
|
src = page << map->log2_page_size;
|
|
len = offset - map->offset;
|
|
|
|
if (ret == -ERR_NOT_FOUND)
|
|
flash_write0(map->dev, dst, len);
|
|
else
|
|
flash_copy(map->dev, dst, src, len);
|
|
|
|
map->offset += len;
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t ftl_write(struct ftl_map *map, uint32_t va, const void *udata,
|
|
size_t len)
|
|
{
|
|
uint32_t dst;
|
|
|
|
if (ftl_seek(map, va) < 0)
|
|
return 0;
|
|
|
|
dst = (map->head << map->log2_page_size) + map->offset;
|
|
len = min(len, (1 << map->log2_page_size) - map->offset);
|
|
|
|
if ((len = flash_write(map->dev, dst, udata, len)) == 0)
|
|
return 0;
|
|
|
|
map->offset += len;
|
|
map->flags |= FTL_DIRTY;
|
|
|
|
return len;
|
|
}
|
|
|
|
int ftl_sync(struct ftl_map *map)
|
|
{
|
|
struct ftl_page_desc page_desc;
|
|
size_t len;
|
|
int ret;
|
|
uint32_t page, dst, src;
|
|
|
|
if (!(map->flags & FTL_DIRTY))
|
|
return 0;
|
|
|
|
if ((ret = trace_path(map, &page_desc, &page, map->last_va)) < 0 &&
|
|
ret != -ERR_NOT_FOUND)
|
|
return -1;
|
|
|
|
dst = (map->head << map->log2_page_size) + map->offset;
|
|
src = (page << map->log2_page_size) + map->offset;
|
|
len = (1 << map->log2_page_size) - map->offset;
|
|
|
|
if (map->flags & FTL_UNMAPPED)
|
|
flash_write0(map->dev, dst, len);
|
|
else
|
|
flash_copy(map->dev, dst, src, len);
|
|
|
|
page_desc.nused_pages = map->nused_pages;
|
|
|
|
if (write_page_desc(map, &page_desc) < 0)
|
|
return -1;
|
|
|
|
map->offset = 0;
|
|
map->flags &= ~(FTL_CACHED_VA | FTL_UNMAPPED | FTL_DIRTY);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ftl_trim(struct ftl_map *map, uint32_t va)
|
|
{
|
|
struct ftl_page_desc page_desc, alt_page_desc;
|
|
size_t level, i;
|
|
uint32_t alt_page, page;
|
|
int ret;
|
|
|
|
if (ftl_sync(map) < 0)
|
|
return -1;
|
|
|
|
if ((ret = trace_path(map, &page_desc, &page, va)) < 0) {
|
|
if (ret == -ERR_NOT_FOUND)
|
|
return 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
--map->nused_pages;
|
|
|
|
for (i = 0; i < 32; ++i) {
|
|
level = 32 - i - 1;
|
|
|
|
if ((alt_page = page_desc.subtrees[level]) != UINT32_MAX)
|
|
break;
|
|
}
|
|
|
|
if (i == 32) {
|
|
map->root = UINT32_MAX;
|
|
|
|
memset(&page_desc, 0xff, sizeof page_desc);
|
|
page_desc.nused_pages = 0;
|
|
|
|
return write_upage(map, NULL, &page_desc);
|
|
}
|
|
|
|
if (read_page_desc(map, &alt_page_desc, alt_page) < 0)
|
|
return -1;
|
|
|
|
page_desc.va = alt_page_desc.va;
|
|
page_desc.nused_pages = map->nused_pages;
|
|
page_desc.subtrees[level] = UINT32_MAX;
|
|
|
|
for (i = level + 1; i < 32; ++i) {
|
|
page_desc.subtrees[i] = alt_page_desc.subtrees[i];
|
|
}
|
|
|
|
if (flash_copy(map->dev, map->head << map->log2_page_size,
|
|
alt_page << map->log2_page_size, 1 << map->log2_page_size) == 0)
|
|
return -1;
|
|
|
|
if (prepare_head(map) < 0)
|
|
return -1;
|
|
|
|
return write_page_desc(map, &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_map *map)
|
|
{
|
|
return map->nused_pages << map->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.
|
|
*/
|
|
uint32_t ftl_get_capacity(const struct ftl_map *map)
|
|
{
|
|
return ((map->nblocks - 1) << map->log2_block_size) -
|
|
((map->nblocks - 1) << map->log2_page_size);
|
|
}
|
|
|