13bd94003SHeinz 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)) { 611c131886SHeinz Mauelshagen DMERR_LIMIT("%s failed: blocknr %llu != wanted %llu", __func__, 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) { 711c131886SHeinz Mauelshagen DMERR_LIMIT("%s failed: csum %u != wanted %u", __func__, 7286a3238cSHeinz Mauelshagen (unsigned int) le32_to_cpu(csum_disk), 7386a3238cSHeinz Mauelshagen (unsigned int) 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, 9886a3238cSHeinz Mauelshagen unsigned int 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, 11286a3238cSHeinz Mauelshagen void (*fn)(void *, const void *, unsigned int)) 1136513c29fSJoe Thornber { 11486a3238cSHeinz Mauelshagen unsigned int nr_entries = le32_to_cpu(ab->nr_entries); 1150ef0b471SHeinz Mauelshagen 116be500ed7SJoe Thornber fn(info->value_type.context, element_at(info, ab, 0), nr_entries); 1176513c29fSJoe Thornber } 1186513c29fSJoe Thornber 1196513c29fSJoe Thornber /* 1206513c29fSJoe Thornber * Increment every value in an array block. 1216513c29fSJoe Thornber */ 1226513c29fSJoe Thornber static void inc_ablock_entries(struct dm_array_info *info, struct array_block *ab) 1236513c29fSJoe Thornber { 1246513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 1256513c29fSJoe Thornber 1266513c29fSJoe Thornber if (vt->inc) 1276513c29fSJoe Thornber on_entries(info, ab, vt->inc); 1286513c29fSJoe Thornber } 1296513c29fSJoe Thornber 1306513c29fSJoe Thornber /* 1316513c29fSJoe Thornber * Decrement every value in an array block. 1326513c29fSJoe Thornber */ 1336513c29fSJoe Thornber static void dec_ablock_entries(struct dm_array_info *info, struct array_block *ab) 1346513c29fSJoe Thornber { 1356513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 1366513c29fSJoe Thornber 1376513c29fSJoe Thornber if (vt->dec) 1386513c29fSJoe Thornber on_entries(info, ab, vt->dec); 1396513c29fSJoe Thornber } 1406513c29fSJoe Thornber 1416513c29fSJoe Thornber /* 1426513c29fSJoe Thornber * Each array block can hold this many values. 1436513c29fSJoe Thornber */ 1446513c29fSJoe Thornber static uint32_t calc_max_entries(size_t value_size, size_t size_of_block) 1456513c29fSJoe Thornber { 1466513c29fSJoe Thornber return (size_of_block - sizeof(struct array_block)) / value_size; 1476513c29fSJoe Thornber } 1486513c29fSJoe Thornber 1496513c29fSJoe Thornber /* 1506513c29fSJoe Thornber * Allocate a new array block. The caller will need to unlock block. 1516513c29fSJoe Thornber */ 1526513c29fSJoe Thornber static int alloc_ablock(struct dm_array_info *info, size_t size_of_block, 1536513c29fSJoe Thornber uint32_t max_entries, 1546513c29fSJoe Thornber struct dm_block **block, struct array_block **ab) 1556513c29fSJoe Thornber { 1566513c29fSJoe Thornber int r; 1576513c29fSJoe Thornber 1586513c29fSJoe Thornber r = dm_tm_new_block(info->btree_info.tm, &array_validator, block); 1596513c29fSJoe Thornber if (r) 1606513c29fSJoe Thornber return r; 1616513c29fSJoe Thornber 1626513c29fSJoe Thornber (*ab) = dm_block_data(*block); 1636513c29fSJoe Thornber (*ab)->max_entries = cpu_to_le32(max_entries); 1646513c29fSJoe Thornber (*ab)->nr_entries = cpu_to_le32(0); 1656513c29fSJoe Thornber (*ab)->value_size = cpu_to_le32(info->value_type.size); 1666513c29fSJoe Thornber 1676513c29fSJoe Thornber return 0; 1686513c29fSJoe Thornber } 1696513c29fSJoe Thornber 1706513c29fSJoe Thornber /* 1716513c29fSJoe Thornber * Pad an array block out with a particular value. Every instance will 1726513c29fSJoe Thornber * cause an increment of the value_type. new_nr must always be more than 1736513c29fSJoe Thornber * the current number of entries. 1746513c29fSJoe Thornber */ 1756513c29fSJoe Thornber static void fill_ablock(struct dm_array_info *info, struct array_block *ab, 17686a3238cSHeinz Mauelshagen const void *value, unsigned int new_nr) 1776513c29fSJoe Thornber { 178be500ed7SJoe Thornber uint32_t nr_entries, delta, i; 1796513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 1806513c29fSJoe Thornber 1816513c29fSJoe Thornber BUG_ON(new_nr > le32_to_cpu(ab->max_entries)); 1826513c29fSJoe Thornber BUG_ON(new_nr < le32_to_cpu(ab->nr_entries)); 1836513c29fSJoe Thornber 1846513c29fSJoe Thornber nr_entries = le32_to_cpu(ab->nr_entries); 185be500ed7SJoe Thornber delta = new_nr - nr_entries; 1866513c29fSJoe Thornber if (vt->inc) 187be500ed7SJoe Thornber vt->inc(vt->context, value, delta); 188be500ed7SJoe Thornber for (i = nr_entries; i < new_nr; i++) 1896513c29fSJoe Thornber memcpy(element_at(info, ab, i), value, vt->size); 1906513c29fSJoe Thornber ab->nr_entries = cpu_to_le32(new_nr); 1916513c29fSJoe Thornber } 1926513c29fSJoe Thornber 1936513c29fSJoe Thornber /* 1946513c29fSJoe Thornber * Remove some entries from the back of an array block. Every value 1956513c29fSJoe Thornber * removed will be decremented. new_nr must be <= the current number of 1966513c29fSJoe Thornber * entries. 1976513c29fSJoe Thornber */ 1986513c29fSJoe Thornber static void trim_ablock(struct dm_array_info *info, struct array_block *ab, 19986a3238cSHeinz Mauelshagen unsigned int new_nr) 2006513c29fSJoe Thornber { 201be500ed7SJoe Thornber uint32_t nr_entries, delta; 2026513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 2036513c29fSJoe Thornber 2046513c29fSJoe Thornber BUG_ON(new_nr > le32_to_cpu(ab->max_entries)); 2056513c29fSJoe Thornber BUG_ON(new_nr > le32_to_cpu(ab->nr_entries)); 2066513c29fSJoe Thornber 2076513c29fSJoe Thornber nr_entries = le32_to_cpu(ab->nr_entries); 208be500ed7SJoe Thornber delta = nr_entries - new_nr; 2096513c29fSJoe Thornber if (vt->dec) 210be500ed7SJoe Thornber vt->dec(vt->context, element_at(info, ab, new_nr - 1), delta); 2116513c29fSJoe Thornber ab->nr_entries = cpu_to_le32(new_nr); 2126513c29fSJoe Thornber } 2136513c29fSJoe Thornber 2146513c29fSJoe Thornber /* 2156513c29fSJoe Thornber * Read locks a block, and coerces it to an array block. The caller must 2166513c29fSJoe Thornber * unlock 'block' when finished. 2176513c29fSJoe Thornber */ 2186513c29fSJoe Thornber static int get_ablock(struct dm_array_info *info, dm_block_t b, 2196513c29fSJoe Thornber struct dm_block **block, struct array_block **ab) 2206513c29fSJoe Thornber { 2216513c29fSJoe Thornber int r; 2226513c29fSJoe Thornber 2236513c29fSJoe Thornber r = dm_tm_read_lock(info->btree_info.tm, b, &array_validator, block); 2246513c29fSJoe Thornber if (r) 2256513c29fSJoe Thornber return r; 2266513c29fSJoe Thornber 2276513c29fSJoe Thornber *ab = dm_block_data(*block); 2286513c29fSJoe Thornber return 0; 2296513c29fSJoe Thornber } 2306513c29fSJoe Thornber 2316513c29fSJoe Thornber /* 2326513c29fSJoe Thornber * Unlocks an array block. 2336513c29fSJoe Thornber */ 2344c7da06fSMikulas Patocka static void unlock_ablock(struct dm_array_info *info, struct dm_block *block) 2356513c29fSJoe Thornber { 2364c7da06fSMikulas Patocka dm_tm_unlock(info->btree_info.tm, block); 2376513c29fSJoe Thornber } 2386513c29fSJoe Thornber 2396513c29fSJoe Thornber /*----------------------------------------------------------------*/ 2406513c29fSJoe Thornber 2416513c29fSJoe Thornber /* 2426513c29fSJoe Thornber * Btree manipulation. 2436513c29fSJoe Thornber */ 2446513c29fSJoe Thornber 2456513c29fSJoe Thornber /* 2466513c29fSJoe Thornber * Looks up an array block in the btree, and then read locks it. 2476513c29fSJoe Thornber * 2486513c29fSJoe Thornber * index is the index of the index of the array_block, (ie. the array index 2496513c29fSJoe Thornber * / max_entries). 2506513c29fSJoe Thornber */ 2516513c29fSJoe Thornber static int lookup_ablock(struct dm_array_info *info, dm_block_t root, 25286a3238cSHeinz Mauelshagen unsigned int index, struct dm_block **block, 2536513c29fSJoe Thornber struct array_block **ab) 2546513c29fSJoe Thornber { 2556513c29fSJoe Thornber int r; 2566513c29fSJoe Thornber uint64_t key = index; 2576513c29fSJoe Thornber __le64 block_le; 2586513c29fSJoe Thornber 2596513c29fSJoe Thornber r = dm_btree_lookup(&info->btree_info, root, &key, &block_le); 2606513c29fSJoe Thornber if (r) 2616513c29fSJoe Thornber return r; 2626513c29fSJoe Thornber 2636513c29fSJoe Thornber return get_ablock(info, le64_to_cpu(block_le), block, ab); 2646513c29fSJoe Thornber } 2656513c29fSJoe Thornber 2666513c29fSJoe Thornber /* 2676513c29fSJoe Thornber * Insert an array block into the btree. The block is _not_ unlocked. 2686513c29fSJoe Thornber */ 2696513c29fSJoe Thornber static int insert_ablock(struct dm_array_info *info, uint64_t index, 2706513c29fSJoe Thornber struct dm_block *block, dm_block_t *root) 2716513c29fSJoe Thornber { 2726513c29fSJoe Thornber __le64 block_le = cpu_to_le64(dm_block_location(block)); 2736513c29fSJoe Thornber 2746513c29fSJoe Thornber __dm_bless_for_disk(block_le); 2756513c29fSJoe Thornber return dm_btree_insert(&info->btree_info, *root, &index, &block_le, root); 2766513c29fSJoe Thornber } 2776513c29fSJoe Thornber 278dd6a77d9SJoe Thornber /*----------------------------------------------------------------*/ 279dd6a77d9SJoe Thornber 280dd6a77d9SJoe Thornber static int __shadow_ablock(struct dm_array_info *info, dm_block_t b, 281dd6a77d9SJoe Thornber struct dm_block **block, struct array_block **ab) 282dd6a77d9SJoe Thornber { 283dd6a77d9SJoe Thornber int inc; 284dd6a77d9SJoe Thornber int r = dm_tm_shadow_block(info->btree_info.tm, b, 285dd6a77d9SJoe Thornber &array_validator, block, &inc); 286dd6a77d9SJoe Thornber if (r) 287dd6a77d9SJoe Thornber return r; 288dd6a77d9SJoe Thornber 289dd6a77d9SJoe Thornber *ab = dm_block_data(*block); 290dd6a77d9SJoe Thornber if (inc) 291dd6a77d9SJoe Thornber inc_ablock_entries(info, *ab); 292dd6a77d9SJoe Thornber 293dd6a77d9SJoe Thornber return 0; 294dd6a77d9SJoe Thornber } 295dd6a77d9SJoe Thornber 296dd6a77d9SJoe Thornber /* 297dd6a77d9SJoe Thornber * The shadow op will often be a noop. Only insert if it really 298dd6a77d9SJoe Thornber * copied data. 299dd6a77d9SJoe Thornber */ 30086a3238cSHeinz Mauelshagen static int __reinsert_ablock(struct dm_array_info *info, unsigned int index, 301dd6a77d9SJoe Thornber struct dm_block *block, dm_block_t b, 302dd6a77d9SJoe Thornber dm_block_t *root) 303dd6a77d9SJoe Thornber { 304dd6a77d9SJoe Thornber int r = 0; 305dd6a77d9SJoe Thornber 306dd6a77d9SJoe Thornber if (dm_block_location(block) != b) { 307dd6a77d9SJoe Thornber /* 308dd6a77d9SJoe Thornber * dm_tm_shadow_block will have already decremented the old 309dd6a77d9SJoe Thornber * block, but it is still referenced by the btree. We 310dd6a77d9SJoe Thornber * increment to stop the insert decrementing it below zero 311dd6a77d9SJoe Thornber * when overwriting the old value. 312dd6a77d9SJoe Thornber */ 313dd6a77d9SJoe Thornber dm_tm_inc(info->btree_info.tm, b); 314dd6a77d9SJoe Thornber r = insert_ablock(info, index, block, root); 315dd6a77d9SJoe Thornber } 316dd6a77d9SJoe Thornber 317dd6a77d9SJoe Thornber return r; 318dd6a77d9SJoe Thornber } 319dd6a77d9SJoe Thornber 3206513c29fSJoe Thornber /* 3216513c29fSJoe Thornber * Looks up an array block in the btree. Then shadows it, and updates the 3226513c29fSJoe Thornber * btree to point to this new shadow. 'root' is an input/output parameter 3236513c29fSJoe Thornber * for both the current root block, and the new one. 3246513c29fSJoe Thornber */ 3256513c29fSJoe Thornber static int shadow_ablock(struct dm_array_info *info, dm_block_t *root, 32686a3238cSHeinz Mauelshagen unsigned int index, struct dm_block **block, 3276513c29fSJoe Thornber struct array_block **ab) 3286513c29fSJoe Thornber { 329dd6a77d9SJoe Thornber int r; 3306513c29fSJoe Thornber uint64_t key = index; 3316513c29fSJoe Thornber dm_block_t b; 3326513c29fSJoe Thornber __le64 block_le; 3336513c29fSJoe Thornber 3346513c29fSJoe Thornber r = dm_btree_lookup(&info->btree_info, *root, &key, &block_le); 3356513c29fSJoe Thornber if (r) 3366513c29fSJoe Thornber return r; 3376513c29fSJoe Thornber b = le64_to_cpu(block_le); 3386513c29fSJoe Thornber 339dd6a77d9SJoe Thornber r = __shadow_ablock(info, b, block, ab); 3406513c29fSJoe Thornber if (r) 3416513c29fSJoe Thornber return r; 3426513c29fSJoe Thornber 343dd6a77d9SJoe Thornber return __reinsert_ablock(info, index, *block, b, root); 3446513c29fSJoe Thornber } 3456513c29fSJoe Thornber 3466513c29fSJoe Thornber /* 3476513c29fSJoe Thornber * Allocate an new array block, and fill it with some values. 3486513c29fSJoe Thornber */ 3496513c29fSJoe Thornber static int insert_new_ablock(struct dm_array_info *info, size_t size_of_block, 3506513c29fSJoe Thornber uint32_t max_entries, 35186a3238cSHeinz Mauelshagen unsigned int block_index, uint32_t nr, 3526513c29fSJoe Thornber const void *value, dm_block_t *root) 3536513c29fSJoe Thornber { 3546513c29fSJoe Thornber int r; 3556513c29fSJoe Thornber struct dm_block *block; 3566513c29fSJoe Thornber struct array_block *ab; 3576513c29fSJoe Thornber 3586513c29fSJoe Thornber r = alloc_ablock(info, size_of_block, max_entries, &block, &ab); 3596513c29fSJoe Thornber if (r) 3606513c29fSJoe Thornber return r; 3616513c29fSJoe Thornber 3626513c29fSJoe Thornber fill_ablock(info, ab, value, nr); 3636513c29fSJoe Thornber r = insert_ablock(info, block_index, block, root); 3646513c29fSJoe Thornber unlock_ablock(info, block); 3656513c29fSJoe Thornber 3666513c29fSJoe Thornber return r; 3676513c29fSJoe Thornber } 3686513c29fSJoe Thornber 3696513c29fSJoe Thornber static int insert_full_ablocks(struct dm_array_info *info, size_t size_of_block, 37086a3238cSHeinz Mauelshagen unsigned int begin_block, unsigned int end_block, 37186a3238cSHeinz Mauelshagen unsigned int max_entries, const void *value, 3726513c29fSJoe Thornber dm_block_t *root) 3736513c29fSJoe Thornber { 3746513c29fSJoe Thornber int r = 0; 3756513c29fSJoe Thornber 3766513c29fSJoe Thornber for (; !r && begin_block != end_block; begin_block++) 3776513c29fSJoe Thornber r = insert_new_ablock(info, size_of_block, max_entries, begin_block, max_entries, value, root); 3786513c29fSJoe Thornber 3796513c29fSJoe Thornber return r; 3806513c29fSJoe Thornber } 3816513c29fSJoe Thornber 3826513c29fSJoe Thornber /* 3836513c29fSJoe Thornber * There are a bunch of functions involved with resizing an array. This 3846513c29fSJoe Thornber * structure holds information that commonly needed by them. Purely here 3856513c29fSJoe Thornber * to reduce parameter count. 3866513c29fSJoe Thornber */ 3876513c29fSJoe Thornber struct resize { 3886513c29fSJoe Thornber /* 3896513c29fSJoe Thornber * Describes the array. 3906513c29fSJoe Thornber */ 3916513c29fSJoe Thornber struct dm_array_info *info; 3926513c29fSJoe Thornber 3936513c29fSJoe Thornber /* 3946513c29fSJoe Thornber * The current root of the array. This gets updated. 3956513c29fSJoe Thornber */ 3966513c29fSJoe Thornber dm_block_t root; 3976513c29fSJoe Thornber 3986513c29fSJoe Thornber /* 3996513c29fSJoe Thornber * Metadata block size. Used to calculate the nr entries in an 4006513c29fSJoe Thornber * array block. 4016513c29fSJoe Thornber */ 4026513c29fSJoe Thornber size_t size_of_block; 4036513c29fSJoe Thornber 4046513c29fSJoe Thornber /* 4056513c29fSJoe Thornber * Maximum nr entries in an array block. 4066513c29fSJoe Thornber */ 40786a3238cSHeinz Mauelshagen unsigned int max_entries; 4086513c29fSJoe Thornber 4096513c29fSJoe Thornber /* 4106513c29fSJoe Thornber * nr of completely full blocks in the array. 4116513c29fSJoe Thornber * 4126513c29fSJoe Thornber * 'old' refers to before the resize, 'new' after. 4136513c29fSJoe Thornber */ 41486a3238cSHeinz Mauelshagen unsigned int old_nr_full_blocks, new_nr_full_blocks; 4156513c29fSJoe Thornber 4166513c29fSJoe Thornber /* 4176513c29fSJoe Thornber * Number of entries in the final block. 0 iff only full blocks in 4186513c29fSJoe Thornber * the array. 4196513c29fSJoe Thornber */ 42086a3238cSHeinz Mauelshagen unsigned int old_nr_entries_in_last_block, new_nr_entries_in_last_block; 4216513c29fSJoe Thornber 4226513c29fSJoe Thornber /* 4236513c29fSJoe Thornber * The default value used when growing the array. 4246513c29fSJoe Thornber */ 4256513c29fSJoe Thornber const void *value; 4266513c29fSJoe Thornber }; 4276513c29fSJoe Thornber 4286513c29fSJoe Thornber /* 4296513c29fSJoe Thornber * Removes a consecutive set of array blocks from the btree. The values 4306513c29fSJoe Thornber * in block are decremented as a side effect of the btree remove. 4316513c29fSJoe Thornber * 4326513c29fSJoe Thornber * begin_index - the index of the first array block to remove. 4336513c29fSJoe Thornber * end_index - the one-past-the-end value. ie. this block is not removed. 4346513c29fSJoe Thornber */ 43586a3238cSHeinz Mauelshagen static int drop_blocks(struct resize *resize, unsigned int begin_index, 43686a3238cSHeinz Mauelshagen unsigned int end_index) 4376513c29fSJoe Thornber { 4386513c29fSJoe Thornber int r; 4396513c29fSJoe Thornber 4406513c29fSJoe Thornber while (begin_index != end_index) { 4416513c29fSJoe Thornber uint64_t key = begin_index++; 4420ef0b471SHeinz Mauelshagen 4436513c29fSJoe Thornber r = dm_btree_remove(&resize->info->btree_info, resize->root, 4446513c29fSJoe Thornber &key, &resize->root); 4456513c29fSJoe Thornber if (r) 4466513c29fSJoe Thornber return r; 4476513c29fSJoe Thornber } 4486513c29fSJoe Thornber 4496513c29fSJoe Thornber return 0; 4506513c29fSJoe Thornber } 4516513c29fSJoe Thornber 4526513c29fSJoe Thornber /* 4536513c29fSJoe Thornber * Calculates how many blocks are needed for the array. 4546513c29fSJoe Thornber */ 45586a3238cSHeinz Mauelshagen static unsigned int total_nr_blocks_needed(unsigned int nr_full_blocks, 45686a3238cSHeinz Mauelshagen unsigned int nr_entries_in_last_block) 4576513c29fSJoe Thornber { 4586513c29fSJoe Thornber return nr_full_blocks + (nr_entries_in_last_block ? 1 : 0); 4596513c29fSJoe Thornber } 4606513c29fSJoe Thornber 4616513c29fSJoe Thornber /* 4626513c29fSJoe Thornber * Shrink an array. 4636513c29fSJoe Thornber */ 4646513c29fSJoe Thornber static int shrink(struct resize *resize) 4656513c29fSJoe Thornber { 4666513c29fSJoe Thornber int r; 46786a3238cSHeinz Mauelshagen unsigned int begin, end; 4686513c29fSJoe Thornber struct dm_block *block; 4696513c29fSJoe Thornber struct array_block *ab; 4706513c29fSJoe Thornber 4716513c29fSJoe Thornber /* 4726513c29fSJoe Thornber * Lose some blocks from the back? 4736513c29fSJoe Thornber */ 4746513c29fSJoe Thornber if (resize->new_nr_full_blocks < resize->old_nr_full_blocks) { 4756513c29fSJoe Thornber begin = total_nr_blocks_needed(resize->new_nr_full_blocks, 4766513c29fSJoe Thornber resize->new_nr_entries_in_last_block); 4776513c29fSJoe Thornber end = total_nr_blocks_needed(resize->old_nr_full_blocks, 4786513c29fSJoe Thornber resize->old_nr_entries_in_last_block); 4796513c29fSJoe Thornber 4806513c29fSJoe Thornber r = drop_blocks(resize, begin, end); 4816513c29fSJoe Thornber if (r) 4826513c29fSJoe Thornber return r; 4836513c29fSJoe Thornber } 4846513c29fSJoe Thornber 4856513c29fSJoe Thornber /* 4866513c29fSJoe Thornber * Trim the new tail block 4876513c29fSJoe Thornber */ 4886513c29fSJoe Thornber if (resize->new_nr_entries_in_last_block) { 4896513c29fSJoe Thornber r = shadow_ablock(resize->info, &resize->root, 4906513c29fSJoe Thornber resize->new_nr_full_blocks, &block, &ab); 4916513c29fSJoe Thornber if (r) 4926513c29fSJoe Thornber return r; 4936513c29fSJoe Thornber 4946513c29fSJoe Thornber trim_ablock(resize->info, ab, resize->new_nr_entries_in_last_block); 4956513c29fSJoe Thornber unlock_ablock(resize->info, block); 4966513c29fSJoe Thornber } 4976513c29fSJoe Thornber 4986513c29fSJoe Thornber return 0; 4996513c29fSJoe Thornber } 5006513c29fSJoe Thornber 5016513c29fSJoe Thornber /* 5026513c29fSJoe Thornber * Grow an array. 5036513c29fSJoe Thornber */ 5046513c29fSJoe Thornber static int grow_extend_tail_block(struct resize *resize, uint32_t new_nr_entries) 5056513c29fSJoe Thornber { 5066513c29fSJoe Thornber int r; 5076513c29fSJoe Thornber struct dm_block *block; 5086513c29fSJoe Thornber struct array_block *ab; 5096513c29fSJoe Thornber 5106513c29fSJoe Thornber r = shadow_ablock(resize->info, &resize->root, 5116513c29fSJoe Thornber resize->old_nr_full_blocks, &block, &ab); 5126513c29fSJoe Thornber if (r) 5136513c29fSJoe Thornber return r; 5146513c29fSJoe Thornber 5156513c29fSJoe Thornber fill_ablock(resize->info, ab, resize->value, new_nr_entries); 5166513c29fSJoe Thornber unlock_ablock(resize->info, block); 5176513c29fSJoe Thornber 5186513c29fSJoe Thornber return r; 5196513c29fSJoe Thornber } 5206513c29fSJoe Thornber 5216513c29fSJoe Thornber static int grow_add_tail_block(struct resize *resize) 5226513c29fSJoe Thornber { 5236513c29fSJoe Thornber return insert_new_ablock(resize->info, resize->size_of_block, 5246513c29fSJoe Thornber resize->max_entries, 5256513c29fSJoe Thornber resize->new_nr_full_blocks, 5266513c29fSJoe Thornber resize->new_nr_entries_in_last_block, 5276513c29fSJoe Thornber resize->value, &resize->root); 5286513c29fSJoe Thornber } 5296513c29fSJoe Thornber 5306513c29fSJoe Thornber static int grow_needs_more_blocks(struct resize *resize) 5316513c29fSJoe Thornber { 5326513c29fSJoe Thornber int r; 53386a3238cSHeinz Mauelshagen unsigned int old_nr_blocks = resize->old_nr_full_blocks; 5346513c29fSJoe Thornber 5356513c29fSJoe Thornber if (resize->old_nr_entries_in_last_block > 0) { 5369c1d4de5SJoe Thornber old_nr_blocks++; 5379c1d4de5SJoe Thornber 5386513c29fSJoe Thornber r = grow_extend_tail_block(resize, resize->max_entries); 5396513c29fSJoe Thornber if (r) 5406513c29fSJoe Thornber return r; 5416513c29fSJoe Thornber } 5426513c29fSJoe Thornber 5436513c29fSJoe Thornber r = insert_full_ablocks(resize->info, resize->size_of_block, 5449c1d4de5SJoe Thornber old_nr_blocks, 5456513c29fSJoe Thornber resize->new_nr_full_blocks, 5466513c29fSJoe Thornber resize->max_entries, resize->value, 5476513c29fSJoe Thornber &resize->root); 5486513c29fSJoe Thornber if (r) 5496513c29fSJoe Thornber return r; 5506513c29fSJoe Thornber 5516513c29fSJoe Thornber if (resize->new_nr_entries_in_last_block) 5526513c29fSJoe Thornber r = grow_add_tail_block(resize); 5536513c29fSJoe Thornber 5546513c29fSJoe Thornber return r; 5556513c29fSJoe Thornber } 5566513c29fSJoe Thornber 5576513c29fSJoe Thornber static int grow(struct resize *resize) 5586513c29fSJoe Thornber { 5596513c29fSJoe Thornber if (resize->new_nr_full_blocks > resize->old_nr_full_blocks) 5606513c29fSJoe Thornber return grow_needs_more_blocks(resize); 5616513c29fSJoe Thornber 5626513c29fSJoe Thornber else if (resize->old_nr_entries_in_last_block) 5636513c29fSJoe Thornber return grow_extend_tail_block(resize, resize->new_nr_entries_in_last_block); 5646513c29fSJoe Thornber 5656513c29fSJoe Thornber else 5666513c29fSJoe Thornber return grow_add_tail_block(resize); 5676513c29fSJoe Thornber } 5686513c29fSJoe Thornber 5696513c29fSJoe Thornber /*----------------------------------------------------------------*/ 5706513c29fSJoe Thornber 5716513c29fSJoe Thornber /* 5726513c29fSJoe Thornber * These are the value_type functions for the btree elements, which point 5736513c29fSJoe Thornber * to array blocks. 5746513c29fSJoe Thornber */ 57586a3238cSHeinz Mauelshagen static void block_inc(void *context, const void *value, unsigned int count) 5766513c29fSJoe Thornber { 577be500ed7SJoe Thornber const __le64 *block_le = value; 5786513c29fSJoe Thornber struct dm_array_info *info = context; 57986a3238cSHeinz Mauelshagen unsigned int i; 5806513c29fSJoe Thornber 581be500ed7SJoe Thornber for (i = 0; i < count; i++, block_le++) 582be500ed7SJoe Thornber dm_tm_inc(info->btree_info.tm, le64_to_cpu(*block_le)); 5836513c29fSJoe Thornber } 5846513c29fSJoe Thornber 585be500ed7SJoe Thornber static void __block_dec(void *context, const void *value) 5866513c29fSJoe Thornber { 5876513c29fSJoe Thornber int r; 5886513c29fSJoe Thornber uint64_t b; 5896513c29fSJoe Thornber __le64 block_le; 5906513c29fSJoe Thornber uint32_t ref_count; 5916513c29fSJoe Thornber struct dm_block *block; 5926513c29fSJoe Thornber struct array_block *ab; 5936513c29fSJoe Thornber struct dm_array_info *info = context; 5946513c29fSJoe Thornber 5956513c29fSJoe Thornber memcpy(&block_le, value, sizeof(block_le)); 5966513c29fSJoe Thornber b = le64_to_cpu(block_le); 5976513c29fSJoe Thornber 5986513c29fSJoe Thornber r = dm_tm_ref(info->btree_info.tm, b, &ref_count); 5996513c29fSJoe Thornber if (r) { 6006513c29fSJoe Thornber DMERR_LIMIT("couldn't get reference count for block %llu", 6016513c29fSJoe Thornber (unsigned long long) b); 6026513c29fSJoe Thornber return; 6036513c29fSJoe Thornber } 6046513c29fSJoe Thornber 6056513c29fSJoe Thornber if (ref_count == 1) { 6066513c29fSJoe Thornber /* 6076513c29fSJoe Thornber * We're about to drop the last reference to this ablock. 6086513c29fSJoe Thornber * So we need to decrement the ref count of the contents. 6096513c29fSJoe Thornber */ 6106513c29fSJoe Thornber r = get_ablock(info, b, &block, &ab); 6116513c29fSJoe Thornber if (r) { 6126513c29fSJoe Thornber DMERR_LIMIT("couldn't get array block %llu", 6136513c29fSJoe Thornber (unsigned long long) b); 6146513c29fSJoe Thornber return; 6156513c29fSJoe Thornber } 6166513c29fSJoe Thornber 6176513c29fSJoe Thornber dec_ablock_entries(info, ab); 6186513c29fSJoe Thornber unlock_ablock(info, block); 6196513c29fSJoe Thornber } 6206513c29fSJoe Thornber 6216513c29fSJoe Thornber dm_tm_dec(info->btree_info.tm, b); 6226513c29fSJoe Thornber } 6236513c29fSJoe Thornber 62486a3238cSHeinz Mauelshagen static void block_dec(void *context, const void *value, unsigned int count) 625be500ed7SJoe Thornber { 62686a3238cSHeinz Mauelshagen unsigned int i; 6270ef0b471SHeinz Mauelshagen 628be500ed7SJoe Thornber for (i = 0; i < count; i++, value += sizeof(__le64)) 629be500ed7SJoe Thornber __block_dec(context, value); 630be500ed7SJoe Thornber } 631be500ed7SJoe Thornber 6326513c29fSJoe Thornber static int block_equal(void *context, const void *value1, const void *value2) 6336513c29fSJoe Thornber { 6346513c29fSJoe Thornber return !memcmp(value1, value2, sizeof(__le64)); 6356513c29fSJoe Thornber } 6366513c29fSJoe Thornber 6376513c29fSJoe Thornber /*----------------------------------------------------------------*/ 6386513c29fSJoe Thornber 6396513c29fSJoe Thornber void dm_array_info_init(struct dm_array_info *info, 6406513c29fSJoe Thornber struct dm_transaction_manager *tm, 6416513c29fSJoe Thornber struct dm_btree_value_type *vt) 6426513c29fSJoe Thornber { 6436513c29fSJoe Thornber struct dm_btree_value_type *bvt = &info->btree_info.value_type; 6446513c29fSJoe Thornber 6456513c29fSJoe Thornber memcpy(&info->value_type, vt, sizeof(info->value_type)); 6466513c29fSJoe Thornber info->btree_info.tm = tm; 6476513c29fSJoe Thornber info->btree_info.levels = 1; 6486513c29fSJoe Thornber 6496513c29fSJoe Thornber bvt->context = info; 6506513c29fSJoe Thornber bvt->size = sizeof(__le64); 6516513c29fSJoe Thornber bvt->inc = block_inc; 6526513c29fSJoe Thornber bvt->dec = block_dec; 6536513c29fSJoe Thornber bvt->equal = block_equal; 6546513c29fSJoe Thornber } 6556513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_info_init); 6566513c29fSJoe Thornber 6576513c29fSJoe Thornber int dm_array_empty(struct dm_array_info *info, dm_block_t *root) 6586513c29fSJoe Thornber { 6596513c29fSJoe Thornber return dm_btree_empty(&info->btree_info, root); 6606513c29fSJoe Thornber } 6616513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_empty); 6626513c29fSJoe Thornber 6636513c29fSJoe Thornber static int array_resize(struct dm_array_info *info, dm_block_t root, 6646513c29fSJoe Thornber uint32_t old_size, uint32_t new_size, 6656513c29fSJoe Thornber const void *value, dm_block_t *new_root) 6666513c29fSJoe Thornber { 6676513c29fSJoe Thornber int r; 6686513c29fSJoe Thornber struct resize resize; 6696513c29fSJoe Thornber 6708001e87dSJoe Thornber if (old_size == new_size) { 6718001e87dSJoe Thornber *new_root = root; 6726513c29fSJoe Thornber return 0; 6738001e87dSJoe Thornber } 6746513c29fSJoe Thornber 6756513c29fSJoe Thornber resize.info = info; 6766513c29fSJoe Thornber resize.root = root; 6776513c29fSJoe Thornber resize.size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm)); 6786513c29fSJoe Thornber resize.max_entries = calc_max_entries(info->value_type.size, 6796513c29fSJoe Thornber resize.size_of_block); 6806513c29fSJoe Thornber 6816513c29fSJoe Thornber resize.old_nr_full_blocks = old_size / resize.max_entries; 6826513c29fSJoe Thornber resize.old_nr_entries_in_last_block = old_size % resize.max_entries; 6836513c29fSJoe Thornber resize.new_nr_full_blocks = new_size / resize.max_entries; 6846513c29fSJoe Thornber resize.new_nr_entries_in_last_block = new_size % resize.max_entries; 6856513c29fSJoe Thornber resize.value = value; 6866513c29fSJoe Thornber 6876513c29fSJoe Thornber r = ((new_size > old_size) ? grow : shrink)(&resize); 6886513c29fSJoe Thornber if (r) 6896513c29fSJoe Thornber return r; 6906513c29fSJoe Thornber 6916513c29fSJoe Thornber *new_root = resize.root; 6926513c29fSJoe Thornber return 0; 6936513c29fSJoe Thornber } 6946513c29fSJoe Thornber 6956513c29fSJoe Thornber int dm_array_resize(struct dm_array_info *info, dm_block_t root, 6966513c29fSJoe Thornber uint32_t old_size, uint32_t new_size, 6976513c29fSJoe Thornber const void *value, dm_block_t *new_root) 6986513c29fSJoe Thornber __dm_written_to_disk(value) 6996513c29fSJoe Thornber { 7006513c29fSJoe Thornber int r = array_resize(info, root, old_size, new_size, value, new_root); 7010ef0b471SHeinz Mauelshagen 7026513c29fSJoe Thornber __dm_unbless_for_disk(value); 7036513c29fSJoe Thornber return r; 7046513c29fSJoe Thornber } 7056513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_resize); 7066513c29fSJoe Thornber 707dd6a77d9SJoe Thornber static int populate_ablock_with_values(struct dm_array_info *info, struct array_block *ab, 70886a3238cSHeinz Mauelshagen value_fn fn, void *context, 70986a3238cSHeinz Mauelshagen unsigned int base, unsigned int new_nr) 710dd6a77d9SJoe Thornber { 711dd6a77d9SJoe Thornber int r; 71286a3238cSHeinz Mauelshagen unsigned int i; 713dd6a77d9SJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 714dd6a77d9SJoe Thornber 715dd6a77d9SJoe Thornber BUG_ON(le32_to_cpu(ab->nr_entries)); 716dd6a77d9SJoe Thornber BUG_ON(new_nr > le32_to_cpu(ab->max_entries)); 717dd6a77d9SJoe Thornber 718dd6a77d9SJoe Thornber for (i = 0; i < new_nr; i++) { 719dd6a77d9SJoe Thornber r = fn(base + i, element_at(info, ab, i), context); 720dd6a77d9SJoe Thornber if (r) 721dd6a77d9SJoe Thornber return r; 722dd6a77d9SJoe Thornber 723dd6a77d9SJoe Thornber if (vt->inc) 724be500ed7SJoe Thornber vt->inc(vt->context, element_at(info, ab, i), 1); 725dd6a77d9SJoe Thornber } 726dd6a77d9SJoe Thornber 727dd6a77d9SJoe Thornber ab->nr_entries = cpu_to_le32(new_nr); 728dd6a77d9SJoe Thornber return 0; 729dd6a77d9SJoe Thornber } 730dd6a77d9SJoe Thornber 731dd6a77d9SJoe Thornber int dm_array_new(struct dm_array_info *info, dm_block_t *root, 732dd6a77d9SJoe Thornber uint32_t size, value_fn fn, void *context) 733dd6a77d9SJoe Thornber { 734dd6a77d9SJoe Thornber int r; 735dd6a77d9SJoe Thornber struct dm_block *block; 736dd6a77d9SJoe Thornber struct array_block *ab; 73786a3238cSHeinz Mauelshagen unsigned int block_index, end_block, size_of_block, max_entries; 738dd6a77d9SJoe Thornber 739dd6a77d9SJoe Thornber r = dm_array_empty(info, root); 740dd6a77d9SJoe Thornber if (r) 741dd6a77d9SJoe Thornber return r; 742dd6a77d9SJoe Thornber 743dd6a77d9SJoe Thornber size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm)); 744dd6a77d9SJoe Thornber max_entries = calc_max_entries(info->value_type.size, size_of_block); 745dd6a77d9SJoe Thornber end_block = dm_div_up(size, max_entries); 746dd6a77d9SJoe Thornber 747dd6a77d9SJoe Thornber for (block_index = 0; block_index != end_block; block_index++) { 748dd6a77d9SJoe Thornber r = alloc_ablock(info, size_of_block, max_entries, &block, &ab); 749dd6a77d9SJoe Thornber if (r) 750dd6a77d9SJoe Thornber break; 751dd6a77d9SJoe Thornber 752dd6a77d9SJoe Thornber r = populate_ablock_with_values(info, ab, fn, context, 753dd6a77d9SJoe Thornber block_index * max_entries, 754dd6a77d9SJoe Thornber min(max_entries, size)); 755dd6a77d9SJoe Thornber if (r) { 756dd6a77d9SJoe Thornber unlock_ablock(info, block); 757dd6a77d9SJoe Thornber break; 758dd6a77d9SJoe Thornber } 759dd6a77d9SJoe Thornber 760dd6a77d9SJoe Thornber r = insert_ablock(info, block_index, block, root); 761dd6a77d9SJoe Thornber unlock_ablock(info, block); 762dd6a77d9SJoe Thornber if (r) 763dd6a77d9SJoe Thornber break; 764dd6a77d9SJoe Thornber 765dd6a77d9SJoe Thornber size -= max_entries; 766dd6a77d9SJoe Thornber } 767dd6a77d9SJoe Thornber 768dd6a77d9SJoe Thornber return r; 769dd6a77d9SJoe Thornber } 770dd6a77d9SJoe Thornber EXPORT_SYMBOL_GPL(dm_array_new); 771dd6a77d9SJoe Thornber 7726513c29fSJoe Thornber int dm_array_del(struct dm_array_info *info, dm_block_t root) 7736513c29fSJoe Thornber { 7746513c29fSJoe Thornber return dm_btree_del(&info->btree_info, root); 7756513c29fSJoe Thornber } 7766513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_del); 7776513c29fSJoe Thornber 7786513c29fSJoe Thornber int dm_array_get_value(struct dm_array_info *info, dm_block_t root, 7796513c29fSJoe Thornber uint32_t index, void *value_le) 7806513c29fSJoe Thornber { 7816513c29fSJoe Thornber int r; 7826513c29fSJoe Thornber struct dm_block *block; 7836513c29fSJoe Thornber struct array_block *ab; 7846513c29fSJoe Thornber size_t size_of_block; 78586a3238cSHeinz Mauelshagen unsigned int entry, max_entries; 7866513c29fSJoe Thornber 7876513c29fSJoe Thornber size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm)); 7886513c29fSJoe Thornber max_entries = calc_max_entries(info->value_type.size, size_of_block); 7896513c29fSJoe Thornber 7906513c29fSJoe Thornber r = lookup_ablock(info, root, index / max_entries, &block, &ab); 7916513c29fSJoe Thornber if (r) 7926513c29fSJoe Thornber return r; 7936513c29fSJoe Thornber 7946513c29fSJoe Thornber entry = index % max_entries; 7956513c29fSJoe Thornber if (entry >= le32_to_cpu(ab->nr_entries)) 7966513c29fSJoe Thornber r = -ENODATA; 7976513c29fSJoe Thornber else 7986513c29fSJoe Thornber memcpy(value_le, element_at(info, ab, entry), 7996513c29fSJoe Thornber info->value_type.size); 8006513c29fSJoe Thornber 8016513c29fSJoe Thornber unlock_ablock(info, block); 8026513c29fSJoe Thornber return r; 8036513c29fSJoe Thornber } 8046513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_get_value); 8056513c29fSJoe Thornber 8066513c29fSJoe Thornber static int array_set_value(struct dm_array_info *info, dm_block_t root, 8076513c29fSJoe Thornber uint32_t index, const void *value, dm_block_t *new_root) 8086513c29fSJoe Thornber { 8096513c29fSJoe Thornber int r; 8106513c29fSJoe Thornber struct dm_block *block; 8116513c29fSJoe Thornber struct array_block *ab; 8126513c29fSJoe Thornber size_t size_of_block; 81386a3238cSHeinz Mauelshagen unsigned int max_entries; 81486a3238cSHeinz Mauelshagen unsigned int entry; 8156513c29fSJoe Thornber void *old_value; 8166513c29fSJoe Thornber struct dm_btree_value_type *vt = &info->value_type; 8176513c29fSJoe Thornber 8186513c29fSJoe Thornber size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm)); 8196513c29fSJoe Thornber max_entries = calc_max_entries(info->value_type.size, size_of_block); 8206513c29fSJoe Thornber 8216513c29fSJoe Thornber r = shadow_ablock(info, &root, index / max_entries, &block, &ab); 8226513c29fSJoe Thornber if (r) 8236513c29fSJoe Thornber return r; 8246513c29fSJoe Thornber *new_root = root; 8256513c29fSJoe Thornber 8266513c29fSJoe Thornber entry = index % max_entries; 8276513c29fSJoe Thornber if (entry >= le32_to_cpu(ab->nr_entries)) { 8286513c29fSJoe Thornber r = -ENODATA; 8296513c29fSJoe Thornber goto out; 8306513c29fSJoe Thornber } 8316513c29fSJoe Thornber 8326513c29fSJoe Thornber old_value = element_at(info, ab, entry); 8336513c29fSJoe Thornber if (vt->dec && 8346513c29fSJoe Thornber (!vt->equal || !vt->equal(vt->context, old_value, value))) { 835be500ed7SJoe Thornber vt->dec(vt->context, old_value, 1); 8366513c29fSJoe Thornber if (vt->inc) 837be500ed7SJoe Thornber vt->inc(vt->context, value, 1); 8386513c29fSJoe Thornber } 8396513c29fSJoe Thornber 8406513c29fSJoe Thornber memcpy(old_value, value, info->value_type.size); 8416513c29fSJoe Thornber 8426513c29fSJoe Thornber out: 8436513c29fSJoe Thornber unlock_ablock(info, block); 8446513c29fSJoe Thornber return r; 8456513c29fSJoe Thornber } 8466513c29fSJoe Thornber 8476513c29fSJoe Thornber int dm_array_set_value(struct dm_array_info *info, dm_block_t root, 8486513c29fSJoe Thornber uint32_t index, const void *value, dm_block_t *new_root) 8496513c29fSJoe Thornber __dm_written_to_disk(value) 8506513c29fSJoe Thornber { 8516513c29fSJoe Thornber int r; 8526513c29fSJoe Thornber 8536513c29fSJoe Thornber r = array_set_value(info, root, index, value, new_root); 8546513c29fSJoe Thornber __dm_unbless_for_disk(value); 8556513c29fSJoe Thornber return r; 8566513c29fSJoe Thornber } 8576513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_set_value); 8586513c29fSJoe Thornber 8596513c29fSJoe Thornber struct walk_info { 8606513c29fSJoe Thornber struct dm_array_info *info; 8616513c29fSJoe Thornber int (*fn)(void *context, uint64_t key, void *leaf); 8626513c29fSJoe Thornber void *context; 8636513c29fSJoe Thornber }; 8646513c29fSJoe Thornber 8656513c29fSJoe Thornber static int walk_ablock(void *context, uint64_t *keys, void *leaf) 8666513c29fSJoe Thornber { 8676513c29fSJoe Thornber struct walk_info *wi = context; 8686513c29fSJoe Thornber 8696513c29fSJoe Thornber int r; 87086a3238cSHeinz Mauelshagen unsigned int i; 8716513c29fSJoe Thornber __le64 block_le; 87286a3238cSHeinz Mauelshagen unsigned int nr_entries, max_entries; 8736513c29fSJoe Thornber struct dm_block *block; 8746513c29fSJoe Thornber struct array_block *ab; 8756513c29fSJoe Thornber 8766513c29fSJoe Thornber memcpy(&block_le, leaf, sizeof(block_le)); 8776513c29fSJoe Thornber r = get_ablock(wi->info, le64_to_cpu(block_le), &block, &ab); 8786513c29fSJoe Thornber if (r) 8796513c29fSJoe Thornber return r; 8806513c29fSJoe Thornber 8816513c29fSJoe Thornber max_entries = le32_to_cpu(ab->max_entries); 8826513c29fSJoe Thornber nr_entries = le32_to_cpu(ab->nr_entries); 8836513c29fSJoe Thornber for (i = 0; i < nr_entries; i++) { 8846513c29fSJoe Thornber r = wi->fn(wi->context, keys[0] * max_entries + i, 8856513c29fSJoe Thornber element_at(wi->info, ab, i)); 8866513c29fSJoe Thornber 8876513c29fSJoe Thornber if (r) 8886513c29fSJoe Thornber break; 8896513c29fSJoe Thornber } 8906513c29fSJoe Thornber 8916513c29fSJoe Thornber unlock_ablock(wi->info, block); 8926513c29fSJoe Thornber return r; 8936513c29fSJoe Thornber } 8946513c29fSJoe Thornber 8956513c29fSJoe Thornber int dm_array_walk(struct dm_array_info *info, dm_block_t root, 8966513c29fSJoe Thornber int (*fn)(void *, uint64_t key, void *leaf), 8976513c29fSJoe Thornber void *context) 8986513c29fSJoe Thornber { 8996513c29fSJoe Thornber struct walk_info wi; 9006513c29fSJoe Thornber 9016513c29fSJoe Thornber wi.info = info; 9026513c29fSJoe Thornber wi.fn = fn; 9036513c29fSJoe Thornber wi.context = context; 9046513c29fSJoe Thornber 9056513c29fSJoe Thornber return dm_btree_walk(&info->btree_info, root, walk_ablock, &wi); 9066513c29fSJoe Thornber } 9076513c29fSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_walk); 9086513c29fSJoe Thornber 9096513c29fSJoe Thornber /*----------------------------------------------------------------*/ 910fdd1315aSJoe Thornber 911fdd1315aSJoe Thornber static int load_ablock(struct dm_array_cursor *c) 912fdd1315aSJoe Thornber { 913fdd1315aSJoe Thornber int r; 914fdd1315aSJoe Thornber __le64 value_le; 915fdd1315aSJoe Thornber uint64_t key; 916fdd1315aSJoe Thornber 917fdd1315aSJoe Thornber if (c->block) 918fdd1315aSJoe Thornber unlock_ablock(c->info, c->block); 919fdd1315aSJoe Thornber 920fdd1315aSJoe Thornber c->index = 0; 921fdd1315aSJoe Thornber 922fdd1315aSJoe Thornber r = dm_btree_cursor_get_value(&c->cursor, &key, &value_le); 923fdd1315aSJoe Thornber if (r) { 924fdd1315aSJoe Thornber DMERR("dm_btree_cursor_get_value failed"); 9256002bec5SMing-Hung Tsai goto out; 926fdd1315aSJoe Thornber 927fdd1315aSJoe Thornber } else { 928fdd1315aSJoe Thornber r = get_ablock(c->info, le64_to_cpu(value_le), &c->block, &c->ab); 929fdd1315aSJoe Thornber if (r) { 930fdd1315aSJoe Thornber DMERR("get_ablock failed"); 9316002bec5SMing-Hung Tsai goto out; 932fdd1315aSJoe Thornber } 933fdd1315aSJoe Thornber } 934fdd1315aSJoe Thornber 9356002bec5SMing-Hung Tsai return 0; 9366002bec5SMing-Hung Tsai 9376002bec5SMing-Hung Tsai out: 9386002bec5SMing-Hung Tsai dm_btree_cursor_end(&c->cursor); 9396002bec5SMing-Hung Tsai c->block = NULL; 9406002bec5SMing-Hung Tsai c->ab = NULL; 941fdd1315aSJoe Thornber return r; 942fdd1315aSJoe Thornber } 943fdd1315aSJoe Thornber 944fdd1315aSJoe Thornber int dm_array_cursor_begin(struct dm_array_info *info, dm_block_t root, 945fdd1315aSJoe Thornber struct dm_array_cursor *c) 946fdd1315aSJoe Thornber { 947fdd1315aSJoe Thornber int r; 948fdd1315aSJoe Thornber 949fdd1315aSJoe Thornber memset(c, 0, sizeof(*c)); 950fdd1315aSJoe Thornber c->info = info; 951fdd1315aSJoe Thornber r = dm_btree_cursor_begin(&info->btree_info, root, true, &c->cursor); 952fdd1315aSJoe Thornber if (r) { 953fdd1315aSJoe Thornber DMERR("couldn't create btree cursor"); 954fdd1315aSJoe Thornber return r; 955fdd1315aSJoe Thornber } 956fdd1315aSJoe Thornber 957fdd1315aSJoe Thornber return load_ablock(c); 958fdd1315aSJoe Thornber } 959fdd1315aSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_begin); 960fdd1315aSJoe Thornber 961fdd1315aSJoe Thornber void dm_array_cursor_end(struct dm_array_cursor *c) 962fdd1315aSJoe Thornber { 963*14f0e64cSMing-Hung Tsai if (c->block) 964fdd1315aSJoe Thornber unlock_ablock(c->info, c->block); 965*14f0e64cSMing-Hung Tsai 966fdd1315aSJoe Thornber dm_btree_cursor_end(&c->cursor); 967fdd1315aSJoe Thornber } 968fdd1315aSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_end); 969fdd1315aSJoe Thornber 970fdd1315aSJoe Thornber int dm_array_cursor_next(struct dm_array_cursor *c) 971fdd1315aSJoe Thornber { 972fdd1315aSJoe Thornber int r; 973fdd1315aSJoe Thornber 974fdd1315aSJoe Thornber if (!c->block) 975fdd1315aSJoe Thornber return -ENODATA; 976fdd1315aSJoe Thornber 977fdd1315aSJoe Thornber c->index++; 978fdd1315aSJoe Thornber 979fdd1315aSJoe Thornber if (c->index >= le32_to_cpu(c->ab->nr_entries)) { 980fdd1315aSJoe Thornber r = dm_btree_cursor_next(&c->cursor); 981fdd1315aSJoe Thornber if (r) 982fdd1315aSJoe Thornber return r; 983fdd1315aSJoe Thornber 984fdd1315aSJoe Thornber r = load_ablock(c); 985fdd1315aSJoe Thornber if (r) 986fdd1315aSJoe Thornber return r; 987fdd1315aSJoe Thornber } 988fdd1315aSJoe Thornber 989fdd1315aSJoe Thornber return 0; 990fdd1315aSJoe Thornber } 991fdd1315aSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_next); 992fdd1315aSJoe Thornber 9939b696229SJoe Thornber int dm_array_cursor_skip(struct dm_array_cursor *c, uint32_t count) 9949b696229SJoe Thornber { 9959b696229SJoe Thornber int r; 9969b696229SJoe Thornber 9979b696229SJoe Thornber do { 9989b696229SJoe Thornber uint32_t remaining = le32_to_cpu(c->ab->nr_entries) - c->index; 9999b696229SJoe Thornber 10009b696229SJoe Thornber if (count < remaining) { 10019b696229SJoe Thornber c->index += count; 10029b696229SJoe Thornber return 0; 10039b696229SJoe Thornber } 10049b696229SJoe Thornber 10059b696229SJoe Thornber count -= remaining; 10069b696229SJoe Thornber r = dm_array_cursor_next(c); 10079b696229SJoe Thornber 10089b696229SJoe Thornber } while (!r); 10099b696229SJoe Thornber 10109b696229SJoe Thornber return r; 10119b696229SJoe Thornber } 10129b696229SJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_skip); 10139b696229SJoe Thornber 1014fdd1315aSJoe Thornber void dm_array_cursor_get_value(struct dm_array_cursor *c, void **value_le) 1015fdd1315aSJoe Thornber { 1016fdd1315aSJoe Thornber *value_le = element_at(c->info, c->ab, c->index); 1017fdd1315aSJoe Thornber } 1018fdd1315aSJoe Thornber EXPORT_SYMBOL_GPL(dm_array_cursor_get_value); 1019fdd1315aSJoe Thornber 1020fdd1315aSJoe Thornber /*----------------------------------------------------------------*/ 1021