#include #include #include #include #include #include #include #include #include #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_start_transaction(struct ftl_map *map, uint32_t va) { uint32_t page; int ret; if ((map->outstanding.flags & FTL_PRESENT) && map->outstanding.va == va) return 0; if (ftl_sync(map) < 0) return -1; if ((ret = trace_path(map, NULL, &page, va)) < 0 && ret != -ERR_NOT_FOUND) return -1; if (prepare_head(map) < 0) return -1; map->outstanding.flags |= FTL_PRESENT; map->outstanding.va = va; map->outstanding.page = page; if (ret == -ERR_NOT_FOUND) { map->outstanding.flags |= FTL_NO_MAPPING; ++map->nused_pages; } map->offset = 0; return 0; } static int ftl_seek(struct ftl_map *map, uint32_t va) { size_t len; uint32_t offset, dst, src; if (va >= ftl_get_capacity(map)) return -1; offset = va & ((1 << map->log2_page_size) - 1); va >>= map->log2_page_size; if (ftl_start_transaction(map, va) < 0) return -1; if (offset < map->offset) return -1; if (offset == map->offset) return 0; dst = map->head << map->log2_page_size; src = map->outstanding.page << map->log2_page_size; len = offset - map->offset; if (map->outstanding.flags & FTL_NO_MAPPING) 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; return len; } int ftl_sync(struct ftl_map *map) { struct ftl_page_desc page_desc; size_t len; int ret; uint32_t dst, src; if (!(map->outstanding.flags & FTL_PRESENT)) return 0; dst = (map->head << map->log2_page_size) + map->offset; src = (map->outstanding.page << map->log2_page_size) + map->offset; len = (1 << map->log2_page_size) - map->offset; if (map->outstanding.flags & FTL_NO_MAPPING) flash_write0(map->dev, dst, len); else flash_copy(map->dev, dst, src, len); page_desc.nused_pages = map->nused_pages; if ((ret = trace_path(map, &page_desc, NULL, map->outstanding.va)) < 0 && ret != -ERR_NOT_FOUND) return -1; if (write_page_desc(map, &page_desc) < 0) return -1; map->offset = 0; map->outstanding.flags = 0; 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; } 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; map->nused_pages = 0; 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; --map->nused_pages; 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); }