1*3bd94003SHeinz Mauelshagen // SPDX-License-Identifier: GPL-2.0-only 26513c29fSJoe Thornber /* 36513c29fSJoe Thornber * Copyright (C) 2012 Red Hat, Inc. 46513c29fSJoe Thornber * 56513c29fSJoe Thornber * This file is released under the GPL. 66513c29fSJoe Thornber */ 76513c29fSJoe Thornber 86513c29fSJoe Thornber #include "dm-array.h" 96513c29fSJoe Thornber #include "dm-space-map.h" 106513c29fSJoe Thornber #include "dm-transaction-manager.h" 116513c29fSJoe Thornber 126513c29fSJoe Thornber #include <linux/export.h> 136513c29fSJoe Thornber #include <linux/device-mapper.h> 146513c29fSJoe Thornber 156513c29fSJoe Thornber #define DM_MSG_PREFIX "array" 166513c29fSJoe Thornber 176513c29fSJoe Thornber /*----------------------------------------------------------------*/ 186513c29fSJoe Thornber 196513c29fSJoe Thornber /* 206513c29fSJoe Thornber * The array is implemented as a fully populated btree, which points to 216513c29fSJoe Thornber * blocks that contain the packed values. This is more space efficient 226513c29fSJoe Thornber * than just using a btree since we don't store 1 key per value. 236513c29fSJoe Thornber */ 246513c29fSJoe Thornber struct array_block { 256513c29fSJoe Thornber __le32 csum; 266513c29fSJoe Thornber __le32 max_entries; 276513c29fSJoe Thornber __le32 nr_entries; 286513c29fSJoe Thornber __le32 value_size; 296513c29fSJoe Thornber __le64 blocknr; /* Block this node is supposed to live in. */ 306513c29fSJoe Thornber } __packed; 316513c29fSJoe Thornber 326513c29fSJoe Thornber /*----------------------------------------------------------------*/ 336513c29fSJoe Thornber 346513c29fSJoe Thornber /* 356513c29fSJoe Thornber * Validator methods. As usual we calculate a checksum, and also write the 366513c29fSJoe Thornber * block location into the header (paranoia about ssds remapping areas by 376513c29fSJoe Thornber * mistake). 386513c29fSJoe Thornber */ 396513c29fSJoe Thornber #define CSUM_XOR 595846735 406513c29fSJoe Thornber 416513c29fSJoe Thornber static void array_block_prepare_for_write(struct dm_block_validator *v, 426513c29fSJoe Thornber struct dm_block *b, 436513c29fSJoe Thornber size_t size_of_block) 446513c29fSJoe Thornber { 456513c29fSJoe Thornber struct array_block *bh_le = dm_block_data(b); 466513c29fSJoe Thornber 476513c29fSJoe Thornber bh_le->blocknr = cpu_to_le64(dm_block_location(b)); 486513c29fSJoe Thornber bh_le->csum = cpu_to_le32(dm_bm_checksum(&bh_le->max_entries, 496513c29fSJoe Thornber size_of_block - sizeof(__le32), 506513c29fSJoe Thornber CSUM_XOR)); 516513c29fSJoe Thornber } 526513c29fSJoe Thornber 536513c29fSJoe Thornber static int array_block_check(struct dm_block_validator *v, 546513c29fSJoe Thornber struct dm_block *b, 556513c29fSJoe Thornber size_t size_of_block) 566513c29fSJoe Thornber { 576513c29fSJoe Thornber struct array_block *bh_le = dm_block_data(b); 586513c29fSJoe Thornber __le32 csum_disk; 596513c29fSJoe Thornber 606513c29fSJoe Thornber if (dm_block_location(b) != le64_to_cpu(bh_le->blocknr)) { 616513c29fSJoe Thornber DMERR_LIMIT("array_block_check failed: blocknr %llu != wanted %llu", 626513c29fSJoe Thornber (unsigned long long) le64_to_cpu(bh_le->blocknr), 636513c29fSJoe Thornber (unsigned long long) dm_block_location(b)); 646513c29fSJoe Thornber return -ENOTBLK; 656513c29fSJoe Thornber } 666513c29fSJoe Thornber 676513c29fSJoe Thornber csum_disk = cpu_to_le32(dm_bm_checksum(&bh_le->max_entries, 686513c29fSJoe Thornber size_of_block - sizeof(__le32), 696513c29fSJoe Thornber CSUM_XOR)); 706513c29fSJoe Thornber if (csum_disk != bh_le->csum) { 716513c29fSJoe Thornber DMERR_LIMIT("array_block_check failed: csum %u != wanted %u", 726513c29fSJoe Thornber (unsigned) le32_to_cpu(csum_disk), 736513c29fSJoe Thornber (unsigned) le32_to_cpu(bh_le->csum)); 746513c29fSJoe Thornber return -EILSEQ; 756513c29fSJoe Thornber } 766513c29fSJoe Thornber 776513c29fSJoe Thornber return 0; 786513c29fSJoe Thornber } 796513c29fSJoe Thornber 806513c29fSJoe Thornber static struct dm_block_validator array_validator = { 816513c29fSJoe Thornber .name = "array", 826513c29fSJoe Thornber .prepare_for_write = array_block_prepare_for_write, 836513c29fSJoe Thornber .check = array_block_check 846513c29fSJoe Thornber }; 856513c29fSJoe Thornber 866513c29fSJoe Thornber /*----------------------------------------------------------------*/ 876513c29fSJoe Thornber 886513c29fSJoe Thornber /* 896513c29fSJoe Thornber * Functions for manipulating the array blocks. 906513c29fSJoe Thornber */ 916513c29fSJoe Thornber 926513c29fSJoe Thornber /* 936513c29fSJoe Thornber * Returns a pointer to a value within an array block. 946513c29fSJoe Thornber * 956513c29fSJoe Thornber * index - The index into _this_ specific block. 966513c29fSJoe Thornber */ 976513c29fSJoe Thornber static void *element_at(struct dm_array_info *info, struct array_block *ab, 986513c29fSJoe Thornber unsigned index) 996513c29fSJoe Thornber { 1006513c29fSJoe Thornber unsigned char *entry = (unsigned char *) (ab + 1); 1016513c29fSJoe Thornber 1026513c29fSJoe Thornber entry += index * info->value_type.size; 1036513c29fSJoe Thornber 1046513c29fSJoe Thornber return entry; 1056513c29fSJoe Thornber } 1066513c29fSJoe Thornber 1076513c29fSJoe Thornber /* 1086513c29fSJoe Thornber * Utility function that calls one of the value_type methods on every value 1096513c29fSJoe Thornber * in an array block. 1106513c29fSJoe Thornber */ 1116513c29fSJoe Thornber static void on_entries(struct dm_array_info *info, struct array_block *ab, 112be500ed7SJoe Thornber void (*fn)(void *, const void *, unsigned)) 1136513c29fSJoe Thornber { 114be500ed7SJoe Thornber unsigned nr_entries = le32_to_cpu(ab->nr_entries); 115be500ed7SJoe Thornber fn(info->value_type.context, element_at(info, ab, 0), nr_entries); 1166513c29fSJoe Thornber } 1176513c29fSJoe Thornber 1186513c29fSJoe Thornber /* 1196513c29fSJoe Thornber * Increment every value in an array block. 1206513c29fSJoe Thornber */ 1216513c29fSJoe Thornber static void inc_ablock_entries(struct dm_array_info *info, struct array_block *ab) 1226513c29fSJoe Thornber { 1236513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 1246513c29fSJoe Thornber 1256513c29fSJoe Thornber if (vt->inc) 1266513c29fSJoe Thornber on_entries(info, ab, vt->inc); 1276513c29fSJoe Thornber } 1286513c29fSJoe Thornber 1296513c29fSJoe Thornber /* 1306513c29fSJoe Thornber * Decrement every value in an array block. 1316513c29fSJoe Thornber */ 1326513c29fSJoe Thornber static void dec_ablock_entries(struct dm_array_info *info, struct array_block *ab) 1336513c29fSJoe Thornber { 1346513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 1356513c29fSJoe Thornber 1366513c29fSJoe Thornber if (vt->dec) 1376513c29fSJoe Thornber on_entries(info, ab, vt->dec); 1386513c29fSJoe Thornber } 1396513c29fSJoe Thornber 1406513c29fSJoe Thornber /* 1416513c29fSJoe Thornber * Each array block can hold this many values. 1426513c29fSJoe Thornber */ 1436513c29fSJoe Thornber static uint32_t calc_max_entries(size_t value_size, size_t size_of_block) 1446513c29fSJoe Thornber { 1456513c29fSJoe Thornber return (size_of_block - sizeof(struct array_block)) / value_size; 1466513c29fSJoe Thornber } 1476513c29fSJoe Thornber 1486513c29fSJoe Thornber /* 1496513c29fSJoe Thornber * Allocate a new array block. The caller will need to unlock block. 1506513c29fSJoe Thornber */ 1516513c29fSJoe Thornber static int alloc_ablock(struct dm_array_info *info, size_t size_of_block, 1526513c29fSJoe Thornber uint32_t max_entries, 1536513c29fSJoe Thornber struct dm_block **block, struct array_block **ab) 1546513c29fSJoe Thornber { 1556513c29fSJoe Thornber int r; 1566513c29fSJoe Thornber 1576513c29fSJoe Thornber r = dm_tm_new_block(info->btree_info.tm, &array_validator, block); 1586513c29fSJoe Thornber if (r) 1596513c29fSJoe Thornber return r; 1606513c29fSJoe Thornber 1616513c29fSJoe Thornber (*ab) = dm_block_data(*block); 1626513c29fSJoe Thornber (*ab)->max_entries = cpu_to_le32(max_entries); 1636513c29fSJoe Thornber (*ab)->nr_entries = cpu_to_le32(0); 1646513c29fSJoe Thornber (*ab)->value_size = cpu_to_le32(info->value_type.size); 1656513c29fSJoe Thornber 1666513c29fSJoe Thornber return 0; 1676513c29fSJoe Thornber } 1686513c29fSJoe Thornber 1696513c29fSJoe Thornber /* 1706513c29fSJoe Thornber * Pad an array block out with a particular value. Every instance will 1716513c29fSJoe Thornber * cause an increment of the value_type. new_nr must always be more than 1726513c29fSJoe Thornber * the current number of entries. 1736513c29fSJoe Thornber */ 1746513c29fSJoe Thornber static void fill_ablock(struct dm_array_info *info, struct array_block *ab, 1756513c29fSJoe Thornber const void *value, unsigned new_nr) 1766513c29fSJoe Thornber { 177be500ed7SJoe Thornber uint32_t nr_entries, delta, i; 1786513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 1796513c29fSJoe Thornber 1806513c29fSJoe Thornber BUG_ON(new_nr > le32_to_cpu(ab->max_entries)); 1816513c29fSJoe Thornber BUG_ON(new_nr < le32_to_cpu(ab->nr_entries)); 1826513c29fSJoe Thornber 1836513c29fSJoe Thornber nr_entries = le32_to_cpu(ab->nr_entries); 184be500ed7SJoe Thornber delta = new_nr - nr_entries; 1856513c29fSJoe Thornber if (vt->inc) 186be500ed7SJoe Thornber vt->inc(vt->context, value, delta); 187be500ed7SJoe Thornber for (i = nr_entries; i < new_nr; i++) 1886513c29fSJoe Thornber memcpy(element_at(info, ab, i), value, vt->size); 1896513c29fSJoe Thornber ab->nr_entries = cpu_to_le32(new_nr); 1906513c29fSJoe Thornber } 1916513c29fSJoe Thornber 1926513c29fSJoe Thornber /* 1936513c29fSJoe Thornber * Remove some entries from the back of an array block. Every value 1946513c29fSJoe Thornber * removed will be decremented. new_nr must be <= the current number of 1956513c29fSJoe Thornber * entries. 1966513c29fSJoe Thornber */ 1976513c29fSJoe Thornber static void trim_ablock(struct dm_array_info *info, struct array_block *ab, 1986513c29fSJoe Thornber unsigned new_nr) 1996513c29fSJoe Thornber { 200be500ed7SJoe Thornber uint32_t nr_entries, delta; 2016513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 2026513c29fSJoe Thornber 2036513c29fSJoe Thornber BUG_ON(new_nr > le32_to_cpu(ab->max_entries)); 2046513c29fSJoe Thornber BUG_ON(new_nr > le32_to_cpu(ab->nr_entries)); 2056513c29fSJoe Thornber 2066513c29fSJoe Thornber nr_entries = le32_to_cpu(ab->nr_entries); 207be500ed7SJoe Thornber delta = nr_entries - new_nr; 2086513c29fSJoe Thornber if (vt->dec) 209be500ed7SJoe Thornber vt->dec(vt->context, element_at(info, ab, new_nr - 1), delta); 2106513c29fSJoe Thornber ab->nr_entries = cpu_to_le32(new_nr); 2116513c29fSJoe Thornber } 2126513c29fSJoe Thornber 2136513c29fSJoe Thornber /* 2146513c29fSJoe Thornber * Read locks a block, and coerces it to an array block. The caller must 2156513c29fSJoe Thornber * unlock 'block' when finished. 2166513c29fSJoe Thornber */ 2176513c29fSJoe Thornber static int get_ablock(struct dm_array_info *info, dm_block_t b, 2186513c29fSJoe Thornber struct dm_block **block, struct array_block **ab) 2196513c29fSJoe Thornber { 2206513c29fSJoe Thornber int r; 2216513c29fSJoe Thornber 2226513c29fSJoe Thornber r = dm_tm_read_lock(info->btree_info.tm, b, &array_validator, block); 2236513c29fSJoe Thornber if (r) 2246513c29fSJoe Thornber return r; 2256513c29fSJoe Thornber 2266513c29fSJoe Thornber *ab = dm_block_data(*block); 2276513c29fSJoe Thornber return 0; 2286513c29fSJoe Thornber } 2296513c29fSJoe Thornber 2306513c29fSJoe Thornber /* 2316513c29fSJoe Thornber * Unlocks an array block. 2326513c29fSJoe Thornber */ 2334c7da06fSMikulas Patocka static void unlock_ablock(struct dm_array_info *info, struct dm_block *block) 2346513c29fSJoe Thornber { 2354c7da06fSMikulas Patocka dm_tm_unlock(info->btree_info.tm, block); 2366513c29fSJoe Thornber } 2376513c29fSJoe Thornber 2386513c29fSJoe Thornber /*----------------------------------------------------------------*/ 2396513c29fSJoe Thornber 2406513c29fSJoe Thornber /* 2416513c29fSJoe Thornber * Btree manipulation. 2426513c29fSJoe Thornber */ 2436513c29fSJoe Thornber 2446513c29fSJoe Thornber /* 2456513c29fSJoe Thornber * Looks up an array block in the btree, and then read locks it. 2466513c29fSJoe Thornber * 2476513c29fSJoe Thornber * index is the index of the index of the array_block, (ie. the array index 2486513c29fSJoe Thornber * / max_entries). 2496513c29fSJoe Thornber */ 2506513c29fSJoe Thornber static int lookup_ablock(struct dm_array_info *info, dm_block_t root, 2516513c29fSJoe Thornber unsigned index, struct dm_block **block, 2526513c29fSJoe Thornber struct array_block **ab) 2536513c29fSJoe Thornber { 2546513c29fSJoe Thornber int r; 2556513c29fSJoe Thornber uint64_t key = index; 2566513c29fSJoe Thornber __le64 block_le; 2576513c29fSJoe Thornber 2586513c29fSJoe Thornber r = dm_btree_lookup(&info->btree_info, root, &key, &block_le); 2596513c29fSJoe Thornber if (r) 2606513c29fSJoe Thornber return r; 2616513c29fSJoe Thornber 2626513c29fSJoe Thornber return get_ablock(info, le64_to_cpu(block_le), block, ab); 2636513c29fSJoe Thornber } 2646513c29fSJoe Thornber 2656513c29fSJoe Thornber /* 2666513c29fSJoe Thornber * Insert an array block into the btree. The block is _not_ unlocked. 2676513c29fSJoe Thornber */ 2686513c29fSJoe Thornber static int insert_ablock(struct dm_array_info *info, uint64_t index, 2696513c29fSJoe Thornber struct dm_block *block, dm_block_t *root) 2706513c29fSJoe Thornber { 2716513c29fSJoe Thornber __le64 block_le = cpu_to_le64(dm_block_location(block)); 2726513c29fSJoe Thornber 2736513c29fSJoe Thornber __dm_bless_for_disk(block_le); 2746513c29fSJoe Thornber return dm_btree_insert(&info->btree_info, *root, &index, &block_le, root); 2756513c29fSJoe Thornber } 2766513c29fSJoe Thornber 277dd6a77d9SJoe Thornber /*----------------------------------------------------------------*/ 278dd6a77d9SJoe Thornber 279dd6a77d9SJoe Thornber static int __shadow_ablock(struct dm_array_info *info, dm_block_t b, 280dd6a77d9SJoe Thornber struct dm_block **block, struct array_block **ab) 281dd6a77d9SJoe Thornber { 282dd6a77d9SJoe Thornber int inc; 283dd6a77d9SJoe Thornber int r = dm_tm_shadow_block(info->btree_info.tm, b, 284dd6a77d9SJoe Thornber &array_validator, block, &inc); 285dd6a77d9SJoe Thornber if (r) 286dd6a77d9SJoe Thornber return r; 287dd6a77d9SJoe Thornber 288dd6a77d9SJoe Thornber *ab = dm_block_data(*block); 289dd6a77d9SJoe Thornber if (inc) 290dd6a77d9SJoe Thornber inc_ablock_entries(info, *ab); 291dd6a77d9SJoe Thornber 292dd6a77d9SJoe Thornber return 0; 293dd6a77d9SJoe Thornber } 294dd6a77d9SJoe Thornber 295dd6a77d9SJoe Thornber /* 296dd6a77d9SJoe Thornber * The shadow op will often be a noop. Only insert if it really 297dd6a77d9SJoe Thornber * copied data. 298dd6a77d9SJoe Thornber */ 299dd6a77d9SJoe Thornber static int __reinsert_ablock(struct dm_array_info *info, unsigned index, 300dd6a77d9SJoe Thornber struct dm_block *block, dm_block_t b, 301dd6a77d9SJoe Thornber dm_block_t *root) 302dd6a77d9SJoe Thornber { 303dd6a77d9SJoe Thornber int r = 0; 304dd6a77d9SJoe Thornber 305dd6a77d9SJoe Thornber if (dm_block_location(block) != b) { 306dd6a77d9SJoe Thornber /* 307dd6a77d9SJoe Thornber * dm_tm_shadow_block will have already decremented the old 308dd6a77d9SJoe Thornber * block, but it is still referenced by the btree. We 309dd6a77d9SJoe Thornber * increment to stop the insert decrementing it below zero 310dd6a77d9SJoe Thornber * when overwriting the old value. 311dd6a77d9SJoe Thornber */ 312dd6a77d9SJoe Thornber dm_tm_inc(info->btree_info.tm, b); 313dd6a77d9SJoe Thornber r = insert_ablock(info, index, block, root); 314dd6a77d9SJoe Thornber } 315dd6a77d9SJoe Thornber 316dd6a77d9SJoe Thornber return r; 317dd6a77d9SJoe Thornber } 318dd6a77d9SJoe Thornber 3196513c29fSJoe Thornber /* 3206513c29fSJoe Thornber * Looks up an array block in the btree. Then shadows it, and updates the 3216513c29fSJoe Thornber * btree to point to this new shadow. 'root' is an input/output parameter 3226513c29fSJoe Thornber * for both the current root block, and the new one. 3236513c29fSJoe Thornber */ 3246513c29fSJoe Thornber static int shadow_ablock(struct dm_array_info *info, dm_block_t *root, 3256513c29fSJoe Thornber unsigned index, struct dm_block **block, 3266513c29fSJoe Thornber struct array_block **ab) 3276513c29fSJoe Thornber { 328dd6a77d9SJoe Thornber int r; 3296513c29fSJoe Thornber uint64_t key = index; 3306513c29fSJoe Thornber dm_block_t b; 3316513c29fSJoe Thornber __le64 block_le; 3326513c29fSJoe Thornber 3336513c29fSJoe Thornber r = dm_btree_lookup(&info->btree_info, *root, &key, &block_le); 3346513c29fSJoe Thornber if (r) 3356513c29fSJoe Thornber return r; 3366513c29fSJoe Thornber b = le64_to_cpu(block_le); 3376513c29fSJoe Thornber 338dd6a77d9SJoe Thornber r = __shadow_ablock(info, b, block, ab); 3396513c29fSJoe Thornber if (r) 3406513c29fSJoe Thornber return r; 3416513c29fSJoe Thornber 342dd6a77d9SJoe Thornber return __reinsert_ablock(info, index, *block, b, root); 3436513c29fSJoe Thornber } 3446513c29fSJoe Thornber 3456513c29fSJoe Thornber /* 3466513c29fSJoe Thornber * Allocate an new array block, and fill it with some values. 3476513c29fSJoe Thornber */ 3486513c29fSJoe Thornber static int insert_new_ablock(struct dm_array_info *info, size_t size_of_block, 3496513c29fSJoe Thornber uint32_t max_entries, 3506513c29fSJoe Thornber unsigned block_index, uint32_t nr, 3516513c29fSJoe Thornber const void *value, dm_block_t *root) 3526513c29fSJoe Thornber { 3536513c29fSJoe Thornber int r; 3546513c29fSJoe Thornber struct dm_block *block; 3556513c29fSJoe Thornber struct array_block *ab; 3566513c29fSJoe Thornber 3576513c29fSJoe Thornber r = alloc_ablock(info, size_of_block, max_entries, &block, &ab); 3586513c29fSJoe Thornber if (r) 3596513c29fSJoe Thornber return r; 3606513c29fSJoe Thornber 3616513c29fSJoe Thornber fill_ablock(info, ab, value, nr); 3626513c29fSJoe Thornber r = insert_ablock(info, block_index, block, root); 3636513c29fSJoe Thornber unlock_ablock(info, block); 3646513c29fSJoe Thornber 3656513c29fSJoe Thornber return r; 3666513c29fSJoe Thornber } 3676513c29fSJoe Thornber 3686513c29fSJoe Thornber static int insert_full_ablocks(struct dm_array_info *info, size_t size_of_block, 3696513c29fSJoe Thornber unsigned begin_block, unsigned end_block, 3706513c29fSJoe Thornber unsigned max_entries, const void *value, 3716513c29fSJoe Thornber dm_block_t *root) 3726513c29fSJoe Thornber { 3736513c29fSJoe Thornber int r = 0; 3746513c29fSJoe Thornber 3756513c29fSJoe Thornber for (; !r && begin_block != end_block; begin_block++) 3766513c29fSJoe Thornber r = insert_new_ablock(info, size_of_block, max_entries, begin_block, max_entries, value, root); 3776513c29fSJoe Thornber 3786513c29fSJoe Thornber return r; 3796513c29fSJoe Thornber } 3806513c29fSJoe Thornber 3816513c29fSJoe Thornber /* 3826513c29fSJoe Thornber * There are a bunch of functions involved with resizing an array. This 3836513c29fSJoe Thornber * structure holds information that commonly needed by them. Purely here 3846513c29fSJoe Thornber * to reduce parameter count. 3856513c29fSJoe Thornber */ 3866513c29fSJoe Thornber struct resize { 3876513c29fSJoe Thornber /* 3886513c29fSJoe Thornber * Describes the array. 3896513c29fSJoe Thornber */ 3906513c29fSJoe Thornber struct dm_array_info *info; 3916513c29fSJoe Thornber 3926513c29fSJoe Thornber /* 3936513c29fSJoe Thornber * The current root of the array. This gets updated. 3946513c29fSJoe Thornber */ 3956513c29fSJoe Thornber dm_block_t root; 3966513c29fSJoe Thornber 3976513c29fSJoe Thornber /* 3986513c29fSJoe Thornber * Metadata block size. Used to calculate the nr entries in an 3996513c29fSJoe Thornber * array block. 4006513c29fSJoe Thornber */ 4016513c29fSJoe Thornber size_t size_of_block; 4026513c29fSJoe Thornber 4036513c29fSJoe Thornber /* 4046513c29fSJoe Thornber * Maximum nr entries in an array block. 4056513c29fSJoe Thornber */ 4066513c29fSJoe Thornber unsigned max_entries; 4076513c29fSJoe Thornber 4086513c29fSJoe Thornber /* 4096513c29fSJoe Thornber * nr of completely full blocks in the array. 4106513c29fSJoe Thornber * 4116513c29fSJoe Thornber * 'old' refers to before the resize, 'new' after. 4126513c29fSJoe Thornber */ 4136513c29fSJoe Thornber unsigned old_nr_full_blocks, new_nr_full_blocks; 4146513c29fSJoe Thornber 4156513c29fSJoe Thornber /* 4166513c29fSJoe Thornber * Number of entries in the final block. 0 iff only full blocks in 4176513c29fSJoe Thornber * the array. 4186513c29fSJoe Thornber */ 4196513c29fSJoe Thornber unsigned old_nr_entries_in_last_block, new_nr_entries_in_last_block; 4206513c29fSJoe Thornber 4216513c29fSJoe Thornber /* 4226513c29fSJoe Thornber * The default value used when growing the array. 4236513c29fSJoe Thornber */ 4246513c29fSJoe Thornber const void *value; 4256513c29fSJoe Thornber }; 4266513c29fSJoe Thornber 4276513c29fSJoe Thornber /* 4286513c29fSJoe Thornber * Removes a consecutive set of array blocks from the btree. The values 4296513c29fSJoe Thornber * in block are decremented as a side effect of the btree remove. 4306513c29fSJoe Thornber * 4316513c29fSJoe Thornber * begin_index - the index of the first array block to remove. 4326513c29fSJoe Thornber * end_index - the one-past-the-end value. ie. this block is not removed. 4336513c29fSJoe Thornber */ 4346513c29fSJoe Thornber static int drop_blocks(struct resize *resize, unsigned begin_index, 4356513c29fSJoe Thornber unsigned end_index) 4366513c29fSJoe Thornber { 4376513c29fSJoe Thornber int r; 4386513c29fSJoe Thornber 4396513c29fSJoe Thornber while (begin_index != end_index) { 4406513c29fSJoe Thornber uint64_t key = begin_index++; 4416513c29fSJoe Thornber r = dm_btree_remove(&resize->info->btree_info, resize->root, 4426513c29fSJoe Thornber &key, &resize->root); 4436513c29fSJoe Thornber if (r) 4446513c29fSJoe Thornber return r; 4456513c29fSJoe Thornber } 4466513c29fSJoe Thornber 4476513c29fSJoe Thornber return 0; 4486513c29fSJoe Thornber } 4496513c29fSJoe Thornber 4506513c29fSJoe Thornber /* 4516513c29fSJoe Thornber * Calculates how many blocks are needed for the array. 4526513c29fSJoe Thornber */ 4536513c29fSJoe Thornber static unsigned total_nr_blocks_needed(unsigned nr_full_blocks, 4546513c29fSJoe Thornber unsigned nr_entries_in_last_block) 4556513c29fSJoe Thornber { 4566513c29fSJoe Thornber return nr_full_blocks + (nr_entries_in_last_block ? 1 : 0); 4576513c29fSJoe Thornber } 4586513c29fSJoe Thornber 4596513c29fSJoe Thornber /* 4606513c29fSJoe Thornber * Shrink an array. 4616513c29fSJoe Thornber */ 4626513c29fSJoe Thornber static int shrink(struct resize *resize) 4636513c29fSJoe Thornber { 4646513c29fSJoe Thornber int r; 4656513c29fSJoe Thornber unsigned begin, end; 4666513c29fSJoe Thornber struct dm_block *block; 4676513c29fSJoe Thornber struct array_block *ab; 4686513c29fSJoe Thornber 4696513c29fSJoe Thornber /* 4706513c29fSJoe Thornber * Lose some blocks from the back? 4716513c29fSJoe Thornber */ 4726513c29fSJoe Thornber if (resize->new_nr_full_blocks < resize->old_nr_full_blocks) { 4736513c29fSJoe Thornber begin = total_nr_blocks_needed(resize->new_nr_full_blocks, 4746513c29fSJoe Thornber resize->new_nr_entries_in_last_block); 4756513c29fSJoe Thornber end = total_nr_blocks_needed(resize->old_nr_full_blocks, 4766513c29fSJoe Thornber resize->old_nr_entries_in_last_block); 4776513c29fSJoe Thornber 4786513c29fSJoe Thornber r = drop_blocks(resize, begin, end); 4796513c29fSJoe Thornber if (r) 4806513c29fSJoe Thornber return r; 4816513c29fSJoe Thornber } 4826513c29fSJoe Thornber 4836513c29fSJoe Thornber /* 4846513c29fSJoe Thornber * Trim the new tail block 4856513c29fSJoe Thornber */ 4866513c29fSJoe Thornber if (resize->new_nr_entries_in_last_block) { 4876513c29fSJoe Thornber r = shadow_ablock(resize->info, &resize->root, 4886513c29fSJoe Thornber resize->new_nr_full_blocks, &block, &ab); 4896513c29fSJoe Thornber if (r) 4906513c29fSJoe Thornber return r; 4916513c29fSJoe Thornber 4926513c29fSJoe Thornber trim_ablock(resize->info, ab, resize->new_nr_entries_in_last_block); 4936513c29fSJoe Thornber unlock_ablock(resize->info, block); 4946513c29fSJoe Thornber } 4956513c29fSJoe Thornber 4966513c29fSJoe Thornber return 0; 4976513c29fSJoe Thornber } 4986513c29fSJoe Thornber 4996513c29fSJoe Thornber /* 5006513c29fSJoe Thornber * Grow an array. 5016513c29fSJoe Thornber */ 5026513c29fSJoe Thornber static int grow_extend_tail_block(struct resize *resize, uint32_t new_nr_entries) 5036513c29fSJoe Thornber { 5046513c29fSJoe Thornber int r; 5056513c29fSJoe Thornber struct dm_block *block; 5066513c29fSJoe Thornber struct array_block *ab; 5076513c29fSJoe Thornber 5086513c29fSJoe Thornber r = shadow_ablock(resize->info, &resize->root, 5096513c29fSJoe Thornber resize->old_nr_full_blocks, &block, &ab); 5106513c29fSJoe Thornber if (r) 5116513c29fSJoe Thornber return r; 5126513c29fSJoe Thornber 5136513c29fSJoe Thornber fill_ablock(resize->info, ab, resize->value, new_nr_entries); 5146513c29fSJoe Thornber unlock_ablock(resize->info, block); 5156513c29fSJoe Thornber 5166513c29fSJoe Thornber return r; 5176513c29fSJoe Thornber } 5186513c29fSJoe Thornber 5196513c29fSJoe Thornber static int grow_add_tail_block(struct resize *resize) 5206513c29fSJoe Thornber { 5216513c29fSJoe Thornber return insert_new_ablock(resize->info, resize->size_of_block, 5226513c29fSJoe Thornber resize->max_entries, 5236513c29fSJoe Thornber resize->new_nr_full_blocks, 5246513c29fSJoe Thornber resize->new_nr_entries_in_last_block, 5256513c29fSJoe Thornber resize->value, &resize->root); 5266513c29fSJoe Thornber } 5276513c29fSJoe Thornber 5286513c29fSJoe Thornber static int grow_needs_more_blocks(struct resize *resize) 5296513c29fSJoe Thornber { 5306513c29fSJoe Thornber int r; 5319c1d4de5SJoe Thornber unsigned old_nr_blocks = resize->old_nr_full_blocks; 5326513c29fSJoe Thornber 5336513c29fSJoe Thornber if (resize->old_nr_entries_in_last_block > 0) { 5349c1d4de5SJoe Thornber old_nr_blocks++; 5359c1d4de5SJoe Thornber 5366513c29fSJoe Thornber r = grow_extend_tail_block(resize, resize->max_entries); 5376513c29fSJoe Thornber if (r) 5386513c29fSJoe Thornber return r; 5396513c29fSJoe Thornber } 5406513c29fSJoe Thornber 5416513c29fSJoe Thornber r = insert_full_ablocks(resize->info, resize->size_of_block, 5429c1d4de5SJoe Thornber old_nr_blocks, 5436513c29fSJoe Thornber resize->new_nr_full_blocks, 5446513c29fSJoe Thornber resize->max_entries, resize->value, 5456513c29fSJoe Thornber &resize->root); 5466513c29fSJoe Thornber if (r) 5476513c29fSJoe Thornber return r; 5486513c29fSJoe Thornber 5496513c29fSJoe Thornber if (resize->new_nr_entries_in_last_block) 5506513c29fSJoe Thornber r = grow_add_tail_block(resize); 5516513c29fSJoe Thornber 5526513c29fSJoe Thornber return r; 5536513c29fSJoe Thornber } 5546513c29fSJoe Thornber 5556513c29fSJoe Thornber static int grow(struct resize *resize) 5566513c29fSJoe Thornber { 5576513c29fSJoe Thornber if (resize->new_nr_full_blocks > resize->old_nr_full_blocks) 5586513c29fSJoe Thornber return grow_needs_more_blocks(resize); 5596513c29fSJoe Thornber 5606513c29fSJoe Thornber else if (resize->old_nr_entries_in_last_block) 5616513c29fSJoe Thornber return grow_extend_tail_block(resize, resize->new_nr_entries_in_last_block); 5626513c29fSJoe Thornber 5636513c29fSJoe Thornber else 5646513c29fSJoe Thornber return grow_add_tail_block(resize); 5656513c29fSJoe Thornber } 5666513c29fSJoe Thornber 5676513c29fSJoe Thornber /*----------------------------------------------------------------*/ 5686513c29fSJoe Thornber 5696513c29fSJoe Thornber /* 5706513c29fSJoe Thornber * These are the value_type functions for the btree elements, which point 5716513c29fSJoe Thornber * to array blocks. 5726513c29fSJoe Thornber */ 573be500ed7SJoe Thornber static void block_inc(void *context, const void *value, unsigned count) 5746513c29fSJoe Thornber { 575be500ed7SJoe Thornber const __le64 *block_le = value; 5766513c29fSJoe Thornber struct dm_array_info *info = context; 577be500ed7SJoe Thornber unsigned i; 5786513c29fSJoe Thornber 579be500ed7SJoe Thornber for (i = 0; i < count; i++, block_le++) 580be500ed7SJoe Thornber dm_tm_inc(info->btree_info.tm, le64_to_cpu(*block_le)); 5816513c29fSJoe Thornber } 5826513c29fSJoe Thornber 583be500ed7SJoe Thornber static void __block_dec(void *context, const void *value) 5846513c29fSJoe Thornber { 5856513c29fSJoe Thornber int r; 5866513c29fSJoe Thornber uint64_t b; 5876513c29fSJoe Thornber __le64 block_le; 5886513c29fSJoe Thornber uint32_t ref_count; 5896513c29fSJoe Thornber struct dm_block *block; 5906513c29fSJoe Thornber struct array_block *ab; 5916513c29fSJoe Thornber struct dm_array_info *info = context; 5926513c29fSJoe Thornber 5936513c29fSJoe Thornber memcpy(&block_le, value, sizeof(block_le)); 5946513c29fSJoe Thornber b = le64_to_cpu(block_le); 5956513c29fSJoe Thornber 5966513c29fSJoe Thornber r = dm_tm_ref(info->btree_info.tm, b, &ref_count); 5976513c29fSJoe Thornber if (r) { 5986513c29fSJoe Thornber DMERR_LIMIT("couldn't get reference count for block %llu", 5996513c29fSJoe Thornber (unsigned long long) b); 6006513c29fSJoe Thornber return; 6016513c29fSJoe Thornber } 6026513c29fSJoe Thornber 6036513c29fSJoe Thornber if (ref_count == 1) { 6046513c29fSJoe Thornber /* 6056513c29fSJoe Thornber * We're about to drop the last reference to this ablock. 6066513c29fSJoe Thornber * So we need to decrement the ref count of the contents. 6076513c29fSJoe Thornber */ 6086513c29fSJoe Thornber r = get_ablock(info, b, &block, &ab); 6096513c29fSJoe Thornber if (r) { 6106513c29fSJoe Thornber DMERR_LIMIT("couldn't get array block %llu", 6116513c29fSJoe Thornber (unsigned long long) b); 6126513c29fSJoe Thornber return; 6136513c29fSJoe Thornber } 6146513c29fSJoe Thornber 6156513c29fSJoe Thornber dec_ablock_entries(info, ab); 6166513c29fSJoe Thornber unlock_ablock(info, block); 6176513c29fSJoe Thornber } 6186513c29fSJoe Thornber 6196513c29fSJoe Thornber dm_tm_dec(info->btree_info.tm, b); 6206513c29fSJoe Thornber } 6216513c29fSJoe Thornber 622be500ed7SJoe Thornber static void block_dec(void *context, const void *value, unsigned count) 623be500ed7SJoe Thornber { 624be500ed7SJoe Thornber unsigned i; 625be500ed7SJoe Thornber for (i = 0; i < count; i++, value += sizeof(__le64)) 626be500ed7SJoe Thornber __block_dec(context, value); 627be500ed7SJoe Thornber } 628be500ed7SJoe Thornber 6296513c29fSJoe Thornber static int block_equal(void *context, const void *value1, const void *value2) 6306513c29fSJoe Thornber { 6316513c29fSJoe Thornber return !memcmp(value1, value2, sizeof(__le64)); 6326513c29fSJoe Thornber } 6336513c29fSJoe Thornber 6346513c29fSJoe Thornber /*----------------------------------------------------------------*/ 6356513c29fSJoe Thornber 6366513c29fSJoe Thornber void dm_array_info_init(struct dm_array_info *info, 6376513c29fSJoe Thornber struct dm_transaction_manager *tm, 6386513c29fSJoe Thornber struct dm_btree_value_type *vt) 6396513c29fSJoe Thornber { 6406513c29fSJoe Thornber struct dm_btree_value_type *bvt = &info->btree_info.value_type; 6416513c29fSJoe Thornber 6426513c29fSJoe Thornber memcpy(&info->value_type, vt, sizeof(info->value_type)); 6436513c29fSJoe Thornber info->btree_info.tm = tm; 6446513c29fSJoe Thornber info->btree_info.levels = 1; 6456513c29fSJoe Thornber 6466513c29fSJoe Thornber bvt->context = info; 6476513c29fSJoe Thornber bvt->size = sizeof(__le64); 6486513c29fSJoe Thornber bvt->inc = block_inc; 6496513c29fSJoe Thornber bvt->dec = block_dec; 6506513c29fSJoe Thornber bvt->equal = block_equal; 6516513c29fSJoe Thornber } 6526513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_info_init); 6536513c29fSJoe Thornber 6546513c29fSJoe Thornber int dm_array_empty(struct dm_array_info *info, dm_block_t *root) 6556513c29fSJoe Thornber { 6566513c29fSJoe Thornber return dm_btree_empty(&info->btree_info, root); 6576513c29fSJoe Thornber } 6586513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_empty); 6596513c29fSJoe Thornber 6606513c29fSJoe Thornber static int array_resize(struct dm_array_info *info, dm_block_t root, 6616513c29fSJoe Thornber uint32_t old_size, uint32_t new_size, 6626513c29fSJoe Thornber const void *value, dm_block_t *new_root) 6636513c29fSJoe Thornber { 6646513c29fSJoe Thornber int r; 6656513c29fSJoe Thornber struct resize resize; 6666513c29fSJoe Thornber 6678001e87dSJoe Thornber if (old_size == new_size) { 6688001e87dSJoe Thornber *new_root = root; 6696513c29fSJoe Thornber return 0; 6708001e87dSJoe Thornber } 6716513c29fSJoe Thornber 6726513c29fSJoe Thornber resize.info = info; 6736513c29fSJoe Thornber resize.root = root; 6746513c29fSJoe Thornber resize.size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm)); 6756513c29fSJoe Thornber resize.max_entries = calc_max_entries(info->value_type.size, 6766513c29fSJoe Thornber resize.size_of_block); 6776513c29fSJoe Thornber 6786513c29fSJoe Thornber resize.old_nr_full_blocks = old_size / resize.max_entries; 6796513c29fSJoe Thornber resize.old_nr_entries_in_last_block = old_size % resize.max_entries; 6806513c29fSJoe Thornber resize.new_nr_full_blocks = new_size / resize.max_entries; 6816513c29fSJoe Thornber resize.new_nr_entries_in_last_block = new_size % resize.max_entries; 6826513c29fSJoe Thornber resize.value = value; 6836513c29fSJoe Thornber 6846513c29fSJoe Thornber r = ((new_size > old_size) ? grow : shrink)(&resize); 6856513c29fSJoe Thornber if (r) 6866513c29fSJoe Thornber return r; 6876513c29fSJoe Thornber 6886513c29fSJoe Thornber *new_root = resize.root; 6896513c29fSJoe Thornber return 0; 6906513c29fSJoe Thornber } 6916513c29fSJoe Thornber 6926513c29fSJoe Thornber int dm_array_resize(struct dm_array_info *info, dm_block_t root, 6936513c29fSJoe Thornber uint32_t old_size, uint32_t new_size, 6946513c29fSJoe Thornber const void *value, dm_block_t *new_root) 6956513c29fSJoe Thornber __dm_written_to_disk(value) 6966513c29fSJoe Thornber { 6976513c29fSJoe Thornber int r = array_resize(info, root, old_size, new_size, value, new_root); 6986513c29fSJoe Thornber __dm_unbless_for_disk(value); 6996513c29fSJoe Thornber return r; 7006513c29fSJoe Thornber } 7016513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_resize); 7026513c29fSJoe Thornber 703dd6a77d9SJoe Thornber static int populate_ablock_with_values(struct dm_array_info *info, struct array_block *ab, 704dd6a77d9SJoe Thornber value_fn fn, void *context, unsigned base, unsigned new_nr) 705dd6a77d9SJoe Thornber { 706dd6a77d9SJoe Thornber int r; 707dd6a77d9SJoe Thornber unsigned i; 708dd6a77d9SJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 709dd6a77d9SJoe Thornber 710dd6a77d9SJoe Thornber BUG_ON(le32_to_cpu(ab->nr_entries)); 711dd6a77d9SJoe Thornber BUG_ON(new_nr > le32_to_cpu(ab->max_entries)); 712dd6a77d9SJoe Thornber 713dd6a77d9SJoe Thornber for (i = 0; i < new_nr; i++) { 714dd6a77d9SJoe Thornber r = fn(base + i, element_at(info, ab, i), context); 715dd6a77d9SJoe Thornber if (r) 716dd6a77d9SJoe Thornber return r; 717dd6a77d9SJoe Thornber 718dd6a77d9SJoe Thornber if (vt->inc) 719be500ed7SJoe Thornber vt->inc(vt->context, element_at(info, ab, i), 1); 720dd6a77d9SJoe Thornber } 721dd6a77d9SJoe Thornber 722dd6a77d9SJoe Thornber ab->nr_entries = cpu_to_le32(new_nr); 723dd6a77d9SJoe Thornber return 0; 724dd6a77d9SJoe Thornber } 725dd6a77d9SJoe Thornber 726dd6a77d9SJoe Thornber int dm_array_new(struct dm_array_info *info, dm_block_t *root, 727dd6a77d9SJoe Thornber uint32_t size, value_fn fn, void *context) 728dd6a77d9SJoe Thornber { 729dd6a77d9SJoe Thornber int r; 730dd6a77d9SJoe Thornber struct dm_block *block; 731dd6a77d9SJoe Thornber struct array_block *ab; 732dd6a77d9SJoe Thornber unsigned block_index, end_block, size_of_block, max_entries; 733dd6a77d9SJoe Thornber 734dd6a77d9SJoe Thornber r = dm_array_empty(info, root); 735dd6a77d9SJoe Thornber if (r) 736dd6a77d9SJoe Thornber return r; 737dd6a77d9SJoe Thornber 738dd6a77d9SJoe Thornber size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm)); 739dd6a77d9SJoe Thornber max_entries = calc_max_entries(info->value_type.size, size_of_block); 740dd6a77d9SJoe Thornber end_block = dm_div_up(size, max_entries); 741dd6a77d9SJoe Thornber 742dd6a77d9SJoe Thornber for (block_index = 0; block_index != end_block; block_index++) { 743dd6a77d9SJoe Thornber r = alloc_ablock(info, size_of_block, max_entries, &block, &ab); 744dd6a77d9SJoe Thornber if (r) 745dd6a77d9SJoe Thornber break; 746dd6a77d9SJoe Thornber 747dd6a77d9SJoe Thornber r = populate_ablock_with_values(info, ab, fn, context, 748dd6a77d9SJoe Thornber block_index * max_entries, 749dd6a77d9SJoe Thornber min(max_entries, size)); 750dd6a77d9SJoe Thornber if (r) { 751dd6a77d9SJoe Thornber unlock_ablock(info, block); 752dd6a77d9SJoe Thornber break; 753dd6a77d9SJoe Thornber } 754dd6a77d9SJoe Thornber 755dd6a77d9SJoe Thornber r = insert_ablock(info, block_index, block, root); 756dd6a77d9SJoe Thornber unlock_ablock(info, block); 757dd6a77d9SJoe Thornber if (r) 758dd6a77d9SJoe Thornber break; 759dd6a77d9SJoe Thornber 760dd6a77d9SJoe Thornber size -= max_entries; 761dd6a77d9SJoe Thornber } 762dd6a77d9SJoe Thornber 763dd6a77d9SJoe Thornber return r; 764dd6a77d9SJoe Thornber } 765dd6a77d9SJoe Thornber EXPORT_SYMBOL_GPL(dm_array_new); 766dd6a77d9SJoe Thornber 7676513c29fSJoe Thornber int dm_array_del(struct dm_array_info *info, dm_block_t root) 7686513c29fSJoe Thornber { 7696513c29fSJoe Thornber return dm_btree_del(&info->btree_info, root); 7706513c29fSJoe Thornber } 7716513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_del); 7726513c29fSJoe Thornber 7736513c29fSJoe Thornber int dm_array_get_value(struct dm_array_info *info, dm_block_t root, 7746513c29fSJoe Thornber uint32_t index, void *value_le) 7756513c29fSJoe Thornber { 7766513c29fSJoe Thornber int r; 7776513c29fSJoe Thornber struct dm_block *block; 7786513c29fSJoe Thornber struct array_block *ab; 7796513c29fSJoe Thornber size_t size_of_block; 7806513c29fSJoe Thornber unsigned entry, max_entries; 7816513c29fSJoe Thornber 7826513c29fSJoe Thornber size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm)); 7836513c29fSJoe Thornber max_entries = calc_max_entries(info->value_type.size, size_of_block); 7846513c29fSJoe Thornber 7856513c29fSJoe Thornber r = lookup_ablock(info, root, index / max_entries, &block, &ab); 7866513c29fSJoe Thornber if (r) 7876513c29fSJoe Thornber return r; 7886513c29fSJoe Thornber 7896513c29fSJoe Thornber entry = index % max_entries; 7906513c29fSJoe Thornber if (entry >= le32_to_cpu(ab->nr_entries)) 7916513c29fSJoe Thornber r = -ENODATA; 7926513c29fSJoe Thornber else 7936513c29fSJoe Thornber memcpy(value_le, element_at(info, ab, entry), 7946513c29fSJoe Thornber info->value_type.size); 7956513c29fSJoe Thornber 7966513c29fSJoe Thornber unlock_ablock(info, block); 7976513c29fSJoe Thornber return r; 7986513c29fSJoe Thornber } 7996513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_get_value); 8006513c29fSJoe Thornber 8016513c29fSJoe Thornber static int array_set_value(struct dm_array_info *info, dm_block_t root, 8026513c29fSJoe Thornber uint32_t index, const void *value, dm_block_t *new_root) 8036513c29fSJoe Thornber { 8046513c29fSJoe Thornber int r; 8056513c29fSJoe Thornber struct dm_block *block; 8066513c29fSJoe Thornber struct array_block *ab; 8076513c29fSJoe Thornber size_t size_of_block; 8086513c29fSJoe Thornber unsigned max_entries; 8096513c29fSJoe Thornber unsigned entry; 8106513c29fSJoe Thornber void *old_value; 8116513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 8126513c29fSJoe Thornber 8136513c29fSJoe Thornber size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm)); 8146513c29fSJoe Thornber max_entries = calc_max_entries(info->value_type.size, size_of_block); 8156513c29fSJoe Thornber 8166513c29fSJoe Thornber r = shadow_ablock(info, &root, index / max_entries, &block, &ab); 8176513c29fSJoe Thornber if (r) 8186513c29fSJoe Thornber return r; 8196513c29fSJoe Thornber *new_root = root; 8206513c29fSJoe Thornber 8216513c29fSJoe Thornber entry = index % max_entries; 8226513c29fSJoe Thornber if (entry >= le32_to_cpu(ab->nr_entries)) { 8236513c29fSJoe Thornber r = -ENODATA; 8246513c29fSJoe Thornber goto out; 8256513c29fSJoe Thornber } 8266513c29fSJoe Thornber 8276513c29fSJoe Thornber old_value = element_at(info, ab, entry); 8286513c29fSJoe Thornber if (vt->dec && 8296513c29fSJoe Thornber (!vt->equal || !vt->equal(vt->context, old_value, value))) { 830be500ed7SJoe Thornber vt->dec(vt->context, old_value, 1); 8316513c29fSJoe Thornber if (vt->inc) 832be500ed7SJoe Thornber vt->inc(vt->context, value, 1); 8336513c29fSJoe Thornber } 8346513c29fSJoe Thornber 8356513c29fSJoe Thornber memcpy(old_value, value, info->value_type.size); 8366513c29fSJoe Thornber 8376513c29fSJoe Thornber out: 8386513c29fSJoe Thornber unlock_ablock(info, block); 8396513c29fSJoe Thornber return r; 8406513c29fSJoe Thornber } 8416513c29fSJoe Thornber 8426513c29fSJoe Thornber int dm_array_set_value(struct dm_array_info *info, dm_block_t root, 8436513c29fSJoe Thornber uint32_t index, const void *value, dm_block_t *new_root) 8446513c29fSJoe Thornber __dm_written_to_disk(value) 8456513c29fSJoe Thornber { 8466513c29fSJoe Thornber int r; 8476513c29fSJoe Thornber 8486513c29fSJoe Thornber r = array_set_value(info, root, index, value, new_root); 8496513c29fSJoe Thornber __dm_unbless_for_disk(value); 8506513c29fSJoe Thornber return r; 8516513c29fSJoe Thornber } 8526513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_set_value); 8536513c29fSJoe Thornber 8546513c29fSJoe Thornber struct walk_info { 8556513c29fSJoe Thornber struct dm_array_info *info; 8566513c29fSJoe Thornber int (*fn)(void *context, uint64_t key, void *leaf); 8576513c29fSJoe Thornber void *context; 8586513c29fSJoe Thornber }; 8596513c29fSJoe Thornber 8606513c29fSJoe Thornber static int walk_ablock(void *context, uint64_t *keys, void *leaf) 8616513c29fSJoe Thornber { 8626513c29fSJoe Thornber struct walk_info *wi = context; 8636513c29fSJoe Thornber 8646513c29fSJoe Thornber int r; 8656513c29fSJoe Thornber unsigned i; 8666513c29fSJoe Thornber __le64 block_le; 8676513c29fSJoe Thornber unsigned nr_entries, max_entries; 8686513c29fSJoe Thornber struct dm_block *block; 8696513c29fSJoe Thornber struct array_block *ab; 8706513c29fSJoe Thornber 8716513c29fSJoe Thornber memcpy(&block_le, leaf, sizeof(block_le)); 8726513c29fSJoe Thornber r = get_ablock(wi->info, le64_to_cpu(block_le), &block, &ab); 8736513c29fSJoe Thornber if (r) 8746513c29fSJoe Thornber return r; 8756513c29fSJoe Thornber 8766513c29fSJoe Thornber max_entries = le32_to_cpu(ab->max_entries); 8776513c29fSJoe Thornber nr_entries = le32_to_cpu(ab->nr_entries); 8786513c29fSJoe Thornber for (i = 0; i < nr_entries; i++) { 8796513c29fSJoe Thornber r = wi->fn(wi->context, keys[0] * max_entries + i, 8806513c29fSJoe Thornber element_at(wi->info, ab, i)); 8816513c29fSJoe Thornber 8826513c29fSJoe Thornber if (r) 8836513c29fSJoe Thornber break; 8846513c29fSJoe Thornber } 8856513c29fSJoe Thornber 8866513c29fSJoe Thornber unlock_ablock(wi->info, block); 8876513c29fSJoe Thornber return r; 8886513c29fSJoe Thornber } 8896513c29fSJoe Thornber 8906513c29fSJoe Thornber int dm_array_walk(struct dm_array_info *info, dm_block_t root, 8916513c29fSJoe Thornber int (*fn)(void *, uint64_t key, void *leaf), 8926513c29fSJoe Thornber void *context) 8936513c29fSJoe Thornber { 8946513c29fSJoe Thornber struct walk_info wi; 8956513c29fSJoe Thornber 8966513c29fSJoe Thornber wi.info = info; 8976513c29fSJoe Thornber wi.fn = fn; 8986513c29fSJoe Thornber wi.context = context; 8996513c29fSJoe Thornber 9006513c29fSJoe Thornber return dm_btree_walk(&info->btree_info, root, walk_ablock, &wi); 9016513c29fSJoe Thornber } 9026513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_walk); 9036513c29fSJoe Thornber 9046513c29fSJoe Thornber /*----------------------------------------------------------------*/ 905fdd1315aSJoe Thornber 906fdd1315aSJoe Thornber static int load_ablock(struct dm_array_cursor *c) 907fdd1315aSJoe Thornber { 908fdd1315aSJoe Thornber int r; 909fdd1315aSJoe Thornber __le64 value_le; 910fdd1315aSJoe Thornber uint64_t key; 911fdd1315aSJoe Thornber 912fdd1315aSJoe Thornber if (c->block) 913fdd1315aSJoe Thornber unlock_ablock(c->info, c->block); 914fdd1315aSJoe Thornber 915fdd1315aSJoe Thornber c->block = NULL; 916fdd1315aSJoe Thornber c->ab = NULL; 917fdd1315aSJoe Thornber c->index = 0; 918fdd1315aSJoe Thornber 919fdd1315aSJoe Thornber r = dm_btree_cursor_get_value(&c->cursor, &key, &value_le); 920fdd1315aSJoe Thornber if (r) { 921fdd1315aSJoe Thornber DMERR("dm_btree_cursor_get_value failed"); 922fdd1315aSJoe Thornber dm_btree_cursor_end(&c->cursor); 923fdd1315aSJoe Thornber 924fdd1315aSJoe Thornber } else { 925fdd1315aSJoe Thornber r = get_ablock(c->info, le64_to_cpu(value_le), &c->block, &c->ab); 926fdd1315aSJoe Thornber if (r) { 927fdd1315aSJoe Thornber DMERR("get_ablock failed"); 928fdd1315aSJoe Thornber dm_btree_cursor_end(&c->cursor); 929fdd1315aSJoe Thornber } 930fdd1315aSJoe Thornber } 931fdd1315aSJoe Thornber 932fdd1315aSJoe Thornber return r; 933fdd1315aSJoe Thornber } 934fdd1315aSJoe Thornber 935fdd1315aSJoe Thornber int dm_array_cursor_begin(struct dm_array_info *info, dm_block_t root, 936fdd1315aSJoe Thornber struct dm_array_cursor *c) 937fdd1315aSJoe Thornber { 938fdd1315aSJoe Thornber int r; 939fdd1315aSJoe Thornber 940fdd1315aSJoe Thornber memset(c, 0, sizeof(*c)); 941fdd1315aSJoe Thornber c->info = info; 942fdd1315aSJoe Thornber r = dm_btree_cursor_begin(&info->btree_info, root, true, &c->cursor); 943fdd1315aSJoe Thornber if (r) { 944fdd1315aSJoe Thornber DMERR("couldn't create btree cursor"); 945fdd1315aSJoe Thornber return r; 946fdd1315aSJoe Thornber } 947fdd1315aSJoe Thornber 948fdd1315aSJoe Thornber return load_ablock(c); 949fdd1315aSJoe Thornber } 950fdd1315aSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_begin); 951fdd1315aSJoe Thornber 952fdd1315aSJoe Thornber void dm_array_cursor_end(struct dm_array_cursor *c) 953fdd1315aSJoe Thornber { 954fdd1315aSJoe Thornber if (c->block) { 955fdd1315aSJoe Thornber unlock_ablock(c->info, c->block); 956fdd1315aSJoe Thornber dm_btree_cursor_end(&c->cursor); 957fdd1315aSJoe Thornber } 958fdd1315aSJoe Thornber } 959fdd1315aSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_end); 960fdd1315aSJoe Thornber 961fdd1315aSJoe Thornber int dm_array_cursor_next(struct dm_array_cursor *c) 962fdd1315aSJoe Thornber { 963fdd1315aSJoe Thornber int r; 964fdd1315aSJoe Thornber 965fdd1315aSJoe Thornber if (!c->block) 966fdd1315aSJoe Thornber return -ENODATA; 967fdd1315aSJoe Thornber 968fdd1315aSJoe Thornber c->index++; 969fdd1315aSJoe Thornber 970fdd1315aSJoe Thornber if (c->index >= le32_to_cpu(c->ab->nr_entries)) { 971fdd1315aSJoe Thornber r = dm_btree_cursor_next(&c->cursor); 972fdd1315aSJoe Thornber if (r) 973fdd1315aSJoe Thornber return r; 974fdd1315aSJoe Thornber 975fdd1315aSJoe Thornber r = load_ablock(c); 976fdd1315aSJoe Thornber if (r) 977fdd1315aSJoe Thornber return r; 978fdd1315aSJoe Thornber } 979fdd1315aSJoe Thornber 980fdd1315aSJoe Thornber return 0; 981fdd1315aSJoe Thornber } 982fdd1315aSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_next); 983fdd1315aSJoe Thornber 9849b696229SJoe Thornber int dm_array_cursor_skip(struct dm_array_cursor *c, uint32_t count) 9859b696229SJoe Thornber { 9869b696229SJoe Thornber int r; 9879b696229SJoe Thornber 9889b696229SJoe Thornber do { 9899b696229SJoe Thornber uint32_t remaining = le32_to_cpu(c->ab->nr_entries) - c->index; 9909b696229SJoe Thornber 9919b696229SJoe Thornber if (count < remaining) { 9929b696229SJoe Thornber c->index += count; 9939b696229SJoe Thornber return 0; 9949b696229SJoe Thornber } 9959b696229SJoe Thornber 9969b696229SJoe Thornber count -= remaining; 9979b696229SJoe Thornber r = dm_array_cursor_next(c); 9989b696229SJoe Thornber 9999b696229SJoe Thornber } while (!r); 10009b696229SJoe Thornber 10019b696229SJoe Thornber return r; 10029b696229SJoe Thornber } 10039b696229SJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_skip); 10049b696229SJoe Thornber 1005fdd1315aSJoe Thornber void dm_array_cursor_get_value(struct dm_array_cursor *c, void **value_le) 1006fdd1315aSJoe Thornber { 1007fdd1315aSJoe Thornber *value_le = element_at(c->info, c->ab, c->index); 1008fdd1315aSJoe Thornber } 1009fdd1315aSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_get_value); 1010fdd1315aSJoe Thornber 1011fdd1315aSJoe Thornber /*----------------------------------------------------------------*/ 1012