18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2011 Red Hat, Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is released under the GPL. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "dm-space-map-common.h" 88c2ecf20Sopenharmony_ci#include "dm-space-map-disk.h" 98c2ecf20Sopenharmony_ci#include "dm-space-map.h" 108c2ecf20Sopenharmony_ci#include "dm-transaction-manager.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/list.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/export.h> 158c2ecf20Sopenharmony_ci#include <linux/device-mapper.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define DM_MSG_PREFIX "space map disk" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * Space map interface. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistruct sm_disk { 258c2ecf20Sopenharmony_ci struct dm_space_map sm; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci struct ll_disk ll; 288c2ecf20Sopenharmony_ci struct ll_disk old_ll; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci dm_block_t begin; 318c2ecf20Sopenharmony_ci dm_block_t nr_allocated_this_transaction; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void sm_disk_destroy(struct dm_space_map *sm) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci kfree(smd); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int sm_disk_extend(struct dm_space_map *sm, dm_block_t extra_blocks) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return sm_ll_extend(&smd->ll, extra_blocks); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int sm_disk_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 518c2ecf20Sopenharmony_ci *count = smd->old_ll.nr_blocks; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return 0; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int sm_disk_get_nr_free(struct dm_space_map *sm, dm_block_t *count) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 598c2ecf20Sopenharmony_ci *count = (smd->old_ll.nr_blocks - smd->old_ll.nr_allocated) - smd->nr_allocated_this_transaction; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int sm_disk_get_count(struct dm_space_map *sm, dm_block_t b, 658c2ecf20Sopenharmony_ci uint32_t *result) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 688c2ecf20Sopenharmony_ci return sm_ll_lookup(&smd->ll, b, result); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int sm_disk_count_is_more_than_one(struct dm_space_map *sm, dm_block_t b, 728c2ecf20Sopenharmony_ci int *result) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci int r; 758c2ecf20Sopenharmony_ci uint32_t count; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci r = sm_disk_get_count(sm, b, &count); 788c2ecf20Sopenharmony_ci if (r) 798c2ecf20Sopenharmony_ci return r; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci *result = count > 1; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int sm_disk_set_count(struct dm_space_map *sm, dm_block_t b, 878c2ecf20Sopenharmony_ci uint32_t count) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int r; 908c2ecf20Sopenharmony_ci uint32_t old_count; 918c2ecf20Sopenharmony_ci enum allocation_event ev; 928c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci r = sm_ll_insert(&smd->ll, b, count, &ev); 958c2ecf20Sopenharmony_ci if (!r) { 968c2ecf20Sopenharmony_ci switch (ev) { 978c2ecf20Sopenharmony_ci case SM_NONE: 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci case SM_ALLOC: 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * This _must_ be free in the prior transaction 1038c2ecf20Sopenharmony_ci * otherwise we've lost atomicity. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci smd->nr_allocated_this_transaction++; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci case SM_FREE: 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * It's only free if it's also free in the last 1118c2ecf20Sopenharmony_ci * transaction. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci r = sm_ll_lookup(&smd->old_ll, b, &old_count); 1148c2ecf20Sopenharmony_ci if (r) 1158c2ecf20Sopenharmony_ci return r; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (!old_count) 1188c2ecf20Sopenharmony_ci smd->nr_allocated_this_transaction--; 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return r; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int sm_disk_inc_block(struct dm_space_map *sm, dm_block_t b) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int r; 1298c2ecf20Sopenharmony_ci enum allocation_event ev; 1308c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci r = sm_ll_inc(&smd->ll, b, &ev); 1338c2ecf20Sopenharmony_ci if (!r && (ev == SM_ALLOC)) 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * This _must_ be free in the prior transaction 1368c2ecf20Sopenharmony_ci * otherwise we've lost atomicity. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci smd->nr_allocated_this_transaction++; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return r; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int sm_disk_dec_block(struct dm_space_map *sm, dm_block_t b) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci int r; 1468c2ecf20Sopenharmony_ci uint32_t old_count; 1478c2ecf20Sopenharmony_ci enum allocation_event ev; 1488c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci r = sm_ll_dec(&smd->ll, b, &ev); 1518c2ecf20Sopenharmony_ci if (!r && (ev == SM_FREE)) { 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * It's only free if it's also free in the last 1548c2ecf20Sopenharmony_ci * transaction. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci r = sm_ll_lookup(&smd->old_ll, b, &old_count); 1578c2ecf20Sopenharmony_ci if (!r && !old_count) 1588c2ecf20Sopenharmony_ci smd->nr_allocated_this_transaction--; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return r; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci int r; 1678c2ecf20Sopenharmony_ci enum allocation_event ev; 1688c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * Any block we allocate has to be free in both the old and current ll. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci r = sm_ll_find_common_free_block(&smd->old_ll, &smd->ll, smd->begin, smd->ll.nr_blocks, b); 1748c2ecf20Sopenharmony_ci if (r == -ENOSPC) { 1758c2ecf20Sopenharmony_ci /* 1768c2ecf20Sopenharmony_ci * There's no free block between smd->begin and the end of the metadata device. 1778c2ecf20Sopenharmony_ci * We search before smd->begin in case something has been freed. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci r = sm_ll_find_common_free_block(&smd->old_ll, &smd->ll, 0, smd->begin, b); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (r) 1838c2ecf20Sopenharmony_ci return r; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci smd->begin = *b + 1; 1868c2ecf20Sopenharmony_ci r = sm_ll_inc(&smd->ll, *b, &ev); 1878c2ecf20Sopenharmony_ci if (!r) { 1888c2ecf20Sopenharmony_ci BUG_ON(ev != SM_ALLOC); 1898c2ecf20Sopenharmony_ci smd->nr_allocated_this_transaction++; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return r; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int sm_disk_commit(struct dm_space_map *sm) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci int r; 1988c2ecf20Sopenharmony_ci dm_block_t nr_free; 1998c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci r = sm_disk_get_nr_free(sm, &nr_free); 2028c2ecf20Sopenharmony_ci if (r) 2038c2ecf20Sopenharmony_ci return r; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci r = sm_ll_commit(&smd->ll); 2068c2ecf20Sopenharmony_ci if (r) 2078c2ecf20Sopenharmony_ci return r; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci memcpy(&smd->old_ll, &smd->ll, sizeof(smd->old_ll)); 2108c2ecf20Sopenharmony_ci smd->nr_allocated_this_transaction = 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci r = sm_disk_get_nr_free(sm, &nr_free); 2138c2ecf20Sopenharmony_ci if (r) 2148c2ecf20Sopenharmony_ci return r; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int sm_disk_root_size(struct dm_space_map *sm, size_t *result) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci *result = sizeof(struct disk_sm_root); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int sm_disk_copy_root(struct dm_space_map *sm, void *where_le, size_t max) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct sm_disk *smd = container_of(sm, struct sm_disk, sm); 2298c2ecf20Sopenharmony_ci struct disk_sm_root root_le; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci root_le.nr_blocks = cpu_to_le64(smd->ll.nr_blocks); 2328c2ecf20Sopenharmony_ci root_le.nr_allocated = cpu_to_le64(smd->ll.nr_allocated); 2338c2ecf20Sopenharmony_ci root_le.bitmap_root = cpu_to_le64(smd->ll.bitmap_root); 2348c2ecf20Sopenharmony_ci root_le.ref_count_root = cpu_to_le64(smd->ll.ref_count_root); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (max < sizeof(root_le)) 2378c2ecf20Sopenharmony_ci return -ENOSPC; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci memcpy(where_le, &root_le, sizeof(root_le)); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/ 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct dm_space_map ops = { 2478c2ecf20Sopenharmony_ci .destroy = sm_disk_destroy, 2488c2ecf20Sopenharmony_ci .extend = sm_disk_extend, 2498c2ecf20Sopenharmony_ci .get_nr_blocks = sm_disk_get_nr_blocks, 2508c2ecf20Sopenharmony_ci .get_nr_free = sm_disk_get_nr_free, 2518c2ecf20Sopenharmony_ci .get_count = sm_disk_get_count, 2528c2ecf20Sopenharmony_ci .count_is_more_than_one = sm_disk_count_is_more_than_one, 2538c2ecf20Sopenharmony_ci .set_count = sm_disk_set_count, 2548c2ecf20Sopenharmony_ci .inc_block = sm_disk_inc_block, 2558c2ecf20Sopenharmony_ci .dec_block = sm_disk_dec_block, 2568c2ecf20Sopenharmony_ci .new_block = sm_disk_new_block, 2578c2ecf20Sopenharmony_ci .commit = sm_disk_commit, 2588c2ecf20Sopenharmony_ci .root_size = sm_disk_root_size, 2598c2ecf20Sopenharmony_ci .copy_root = sm_disk_copy_root, 2608c2ecf20Sopenharmony_ci .register_threshold_callback = NULL 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistruct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm, 2648c2ecf20Sopenharmony_ci dm_block_t nr_blocks) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci int r; 2678c2ecf20Sopenharmony_ci struct sm_disk *smd; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci smd = kmalloc(sizeof(*smd), GFP_KERNEL); 2708c2ecf20Sopenharmony_ci if (!smd) 2718c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci smd->begin = 0; 2748c2ecf20Sopenharmony_ci smd->nr_allocated_this_transaction = 0; 2758c2ecf20Sopenharmony_ci memcpy(&smd->sm, &ops, sizeof(smd->sm)); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci r = sm_ll_new_disk(&smd->ll, tm); 2788c2ecf20Sopenharmony_ci if (r) 2798c2ecf20Sopenharmony_ci goto bad; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci r = sm_ll_extend(&smd->ll, nr_blocks); 2828c2ecf20Sopenharmony_ci if (r) 2838c2ecf20Sopenharmony_ci goto bad; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci r = sm_disk_commit(&smd->sm); 2868c2ecf20Sopenharmony_ci if (r) 2878c2ecf20Sopenharmony_ci goto bad; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return &smd->sm; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cibad: 2928c2ecf20Sopenharmony_ci kfree(smd); 2938c2ecf20Sopenharmony_ci return ERR_PTR(r); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_sm_disk_create); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistruct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm, 2988c2ecf20Sopenharmony_ci void *root_le, size_t len) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci int r; 3018c2ecf20Sopenharmony_ci struct sm_disk *smd; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci smd = kmalloc(sizeof(*smd), GFP_KERNEL); 3048c2ecf20Sopenharmony_ci if (!smd) 3058c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci smd->begin = 0; 3088c2ecf20Sopenharmony_ci smd->nr_allocated_this_transaction = 0; 3098c2ecf20Sopenharmony_ci memcpy(&smd->sm, &ops, sizeof(smd->sm)); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci r = sm_ll_open_disk(&smd->ll, tm, root_le, len); 3128c2ecf20Sopenharmony_ci if (r) 3138c2ecf20Sopenharmony_ci goto bad; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci r = sm_disk_commit(&smd->sm); 3168c2ecf20Sopenharmony_ci if (r) 3178c2ecf20Sopenharmony_ci goto bad; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return &smd->sm; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cibad: 3228c2ecf20Sopenharmony_ci kfree(smd); 3238c2ecf20Sopenharmony_ci return ERR_PTR(r); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_sm_disk_open); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/ 328