#include #include #include #include "gc.h" int read_page_desc(struct ftl_map *map, struct ftl_page_desc *page_desc, uint32_t upage); int trace_path(struct ftl_map *map, struct ftl_page_desc *new_page_desc, uint32_t *loc, uint32_t va); int write_upage(struct ftl_map *map, const uint8_t *page, const struct ftl_page_desc *page_desc); /* For a given user page, attempt to claim more free space by checking if no * recent mapping has obsoleted the older mapping. If a more recent mapping * exists, the page can be safely ignored and erased. Otherwise, we preserve * the page by copying the page to create a new mapping such that the old page * can be ignored and erased. */ static int free_page(struct ftl_map *map, uint32_t upage) { struct ftl_page_desc page_desc; uint32_t found_upage, va; if (read_page_desc(map, &page_desc, upage) < 0) return -1; va = page_desc.va; if (trace_path(map, &page_desc, &found_upage, va) < 0) return -1; if (upage != found_upage) return 0; page_desc.nused_pages = map->nused_pages; if (flash_copy(map->dev, map->head << map->log2_page_size, upage << map->log2_page_size, 1 << map->log2_page_size) == 0) return -1; return write_upage(map, NULL, &page_desc); } /* Claim more free space by checking which user pages in a page group are * mapped and for which the mappings have been obsoleted by a more recent * mapping. The mapped user pages are preserved by copying. */ static int free_group(struct ftl_map *map, uint32_t group) { uint32_t npages = UINT32_C(1) << map->log2_pages_per_group; uint32_t page = group << map->log2_pages_per_group; uint32_t i; for (i = 0; i < npages; ++i) { if (free_page(map, page + i) < 0) return -1; } return 0; } /* Claim more free space by checking which user pages in a block are mapped and * for which the mappings have been obsoleted by a more recent mapping. The * mapped user pages are preserved by copying. */ static int free_block(struct ftl_map *map, uint32_t block) { uint32_t ngroups = UINT32_C(1) << map->log2_groups_per_block; uint32_t group = block << map->log2_groups_per_block; uint32_t i; for (i = 0; i < ngroups; ++i) { if (free_group(map, group + i) < 0) return -1; } return 0; } /* Checks if there are sufficient pages available for writing. Otherwise this * function attempts to claim more free space from unmapped pages for which * newer pages have obsoleted the mapping. Further, we move the user pages that * are still mapped as these should be preserved. */ static int free_tail(struct ftl_map *map) { size_t log2_pages_per_block = map->log2_pages_per_group + map->log2_groups_per_block; size_t npages = map->nblocks << log2_pages_per_block; size_t dist; if (map->head < map->tail) dist = map->tail - map->head; else dist = npages - map->head + map->tail; if (dist > (UINT32_C(1) << log2_pages_per_block)) return 0; if (free_block(map, map->tail >> log2_pages_per_block) < 0) return -1; map->tail += 1 << log2_pages_per_block; if (map->tail >= npages) map->tail -= npages; return 0; } /* Prepare the head for writing. If the user page to be written to is not * aligned on a block boundary, the block must already be erased and there is * nothing to be done. Otherwise, we free the tail if necessary and erase the * block for writing. */ int prepare_head(struct ftl_map *map) { size_t log2_pages_per_block = map->log2_pages_per_group + map->log2_groups_per_block; if (!is_aligned(map->head, log2_pages_per_block)) return 0; if (free_tail(map) < 0) return -1; return flash_erase(map->dev, map->head, 1 << log2_pages_per_block); }