13bd94003SHeinz Mauelshagen // SPDX-License-Identifier: GPL-2.0-only
27a87edfeSJoe Thornber /*
37a87edfeSJoe Thornber * Copyright (C) 2012 Red Hat, Inc.
47a87edfeSJoe Thornber *
57a87edfeSJoe Thornber * This file is released under the GPL.
67a87edfeSJoe Thornber */
77a87edfeSJoe Thornber
87a87edfeSJoe Thornber #include "dm-bitset.h"
97a87edfeSJoe Thornber #include "dm-transaction-manager.h"
107a87edfeSJoe Thornber
117a87edfeSJoe Thornber #include <linux/export.h>
127a87edfeSJoe Thornber #include <linux/device-mapper.h>
137a87edfeSJoe Thornber
147a87edfeSJoe Thornber #define DM_MSG_PREFIX "bitset"
157a87edfeSJoe Thornber #define BITS_PER_ARRAY_ENTRY 64
167a87edfeSJoe Thornber
177a87edfeSJoe Thornber /*----------------------------------------------------------------*/
187a87edfeSJoe Thornber
197a87edfeSJoe Thornber static struct dm_btree_value_type bitset_bvt = {
207a87edfeSJoe Thornber .context = NULL,
217a87edfeSJoe Thornber .size = sizeof(__le64),
227a87edfeSJoe Thornber .inc = NULL,
237a87edfeSJoe Thornber .dec = NULL,
247a87edfeSJoe Thornber .equal = NULL,
257a87edfeSJoe Thornber };
267a87edfeSJoe Thornber
277a87edfeSJoe Thornber /*----------------------------------------------------------------*/
287a87edfeSJoe Thornber
dm_disk_bitset_init(struct dm_transaction_manager * tm,struct dm_disk_bitset * info)297a87edfeSJoe Thornber void dm_disk_bitset_init(struct dm_transaction_manager *tm,
307a87edfeSJoe Thornber struct dm_disk_bitset *info)
317a87edfeSJoe Thornber {
327a87edfeSJoe Thornber dm_array_info_init(&info->array_info, tm, &bitset_bvt);
337a87edfeSJoe Thornber info->current_index_set = false;
347a87edfeSJoe Thornber }
357a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_disk_bitset_init);
367a87edfeSJoe Thornber
dm_bitset_empty(struct dm_disk_bitset * info,dm_block_t * root)377a87edfeSJoe Thornber int dm_bitset_empty(struct dm_disk_bitset *info, dm_block_t *root)
387a87edfeSJoe Thornber {
397a87edfeSJoe Thornber return dm_array_empty(&info->array_info, root);
407a87edfeSJoe Thornber }
417a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_empty);
427a87edfeSJoe Thornber
432151249eSJoe Thornber struct packer_context {
442151249eSJoe Thornber bit_value_fn fn;
4586a3238cSHeinz Mauelshagen unsigned int nr_bits;
462151249eSJoe Thornber void *context;
472151249eSJoe Thornber };
482151249eSJoe Thornber
pack_bits(uint32_t index,void * value,void * context)492151249eSJoe Thornber static int pack_bits(uint32_t index, void *value, void *context)
502151249eSJoe Thornber {
512151249eSJoe Thornber int r;
522151249eSJoe Thornber struct packer_context *p = context;
5386a3238cSHeinz Mauelshagen unsigned int bit, nr = min(64u, p->nr_bits - (index * 64));
542151249eSJoe Thornber uint64_t word = 0;
552151249eSJoe Thornber bool bv;
562151249eSJoe Thornber
572151249eSJoe Thornber for (bit = 0; bit < nr; bit++) {
582151249eSJoe Thornber r = p->fn(index * 64 + bit, &bv, p->context);
592151249eSJoe Thornber if (r)
602151249eSJoe Thornber return r;
612151249eSJoe Thornber
622151249eSJoe Thornber if (bv)
632151249eSJoe Thornber set_bit(bit, (unsigned long *) &word);
642151249eSJoe Thornber else
652151249eSJoe Thornber clear_bit(bit, (unsigned long *) &word);
662151249eSJoe Thornber }
672151249eSJoe Thornber
682151249eSJoe Thornber *((__le64 *) value) = cpu_to_le64(word);
692151249eSJoe Thornber
702151249eSJoe Thornber return 0;
712151249eSJoe Thornber }
722151249eSJoe Thornber
dm_bitset_new(struct dm_disk_bitset * info,dm_block_t * root,uint32_t size,bit_value_fn fn,void * context)732151249eSJoe Thornber int dm_bitset_new(struct dm_disk_bitset *info, dm_block_t *root,
742151249eSJoe Thornber uint32_t size, bit_value_fn fn, void *context)
752151249eSJoe Thornber {
762151249eSJoe Thornber struct packer_context p;
77*0ef0b471SHeinz Mauelshagen
782151249eSJoe Thornber p.fn = fn;
792151249eSJoe Thornber p.nr_bits = size;
802151249eSJoe Thornber p.context = context;
812151249eSJoe Thornber
822151249eSJoe Thornber return dm_array_new(&info->array_info, root, dm_div_up(size, 64), pack_bits, &p);
832151249eSJoe Thornber }
842151249eSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_new);
852151249eSJoe Thornber
dm_bitset_resize(struct dm_disk_bitset * info,dm_block_t root,uint32_t old_nr_entries,uint32_t new_nr_entries,bool default_value,dm_block_t * new_root)867a87edfeSJoe Thornber int dm_bitset_resize(struct dm_disk_bitset *info, dm_block_t root,
877a87edfeSJoe Thornber uint32_t old_nr_entries, uint32_t new_nr_entries,
887a87edfeSJoe Thornber bool default_value, dm_block_t *new_root)
897a87edfeSJoe Thornber {
907a87edfeSJoe Thornber uint32_t old_blocks = dm_div_up(old_nr_entries, BITS_PER_ARRAY_ENTRY);
917a87edfeSJoe Thornber uint32_t new_blocks = dm_div_up(new_nr_entries, BITS_PER_ARRAY_ENTRY);
927a87edfeSJoe Thornber __le64 value = default_value ? cpu_to_le64(~0) : cpu_to_le64(0);
937a87edfeSJoe Thornber
947a87edfeSJoe Thornber __dm_bless_for_disk(&value);
957a87edfeSJoe Thornber return dm_array_resize(&info->array_info, root, old_blocks, new_blocks,
967a87edfeSJoe Thornber &value, new_root);
977a87edfeSJoe Thornber }
987a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_resize);
997a87edfeSJoe Thornber
dm_bitset_del(struct dm_disk_bitset * info,dm_block_t root)1007a87edfeSJoe Thornber int dm_bitset_del(struct dm_disk_bitset *info, dm_block_t root)
1017a87edfeSJoe Thornber {
1027a87edfeSJoe Thornber return dm_array_del(&info->array_info, root);
1037a87edfeSJoe Thornber }
1047a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_del);
1057a87edfeSJoe Thornber
dm_bitset_flush(struct dm_disk_bitset * info,dm_block_t root,dm_block_t * new_root)1067a87edfeSJoe Thornber int dm_bitset_flush(struct dm_disk_bitset *info, dm_block_t root,
1077a87edfeSJoe Thornber dm_block_t *new_root)
1087a87edfeSJoe Thornber {
1097a87edfeSJoe Thornber int r;
1107a87edfeSJoe Thornber __le64 value;
1117a87edfeSJoe Thornber
112428e4698SJoe Thornber if (!info->current_index_set || !info->dirty)
1137a87edfeSJoe Thornber return 0;
1147a87edfeSJoe Thornber
1157a87edfeSJoe Thornber value = cpu_to_le64(info->current_bits);
1167a87edfeSJoe Thornber
1177a87edfeSJoe Thornber __dm_bless_for_disk(&value);
1187a87edfeSJoe Thornber r = dm_array_set_value(&info->array_info, root, info->current_index,
1197a87edfeSJoe Thornber &value, new_root);
1207a87edfeSJoe Thornber if (r)
1217a87edfeSJoe Thornber return r;
1227a87edfeSJoe Thornber
1237a87edfeSJoe Thornber info->current_index_set = false;
124428e4698SJoe Thornber info->dirty = false;
125428e4698SJoe Thornber
1267a87edfeSJoe Thornber return 0;
1277a87edfeSJoe Thornber }
1287a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_flush);
1297a87edfeSJoe Thornber
read_bits(struct dm_disk_bitset * info,dm_block_t root,uint32_t array_index)1307a87edfeSJoe Thornber static int read_bits(struct dm_disk_bitset *info, dm_block_t root,
1317a87edfeSJoe Thornber uint32_t array_index)
1327a87edfeSJoe Thornber {
1337a87edfeSJoe Thornber int r;
1347a87edfeSJoe Thornber __le64 value;
1357a87edfeSJoe Thornber
1367a87edfeSJoe Thornber r = dm_array_get_value(&info->array_info, root, array_index, &value);
1377a87edfeSJoe Thornber if (r)
1387a87edfeSJoe Thornber return r;
1397a87edfeSJoe Thornber
1407a87edfeSJoe Thornber info->current_bits = le64_to_cpu(value);
1417a87edfeSJoe Thornber info->current_index_set = true;
1427a87edfeSJoe Thornber info->current_index = array_index;
143428e4698SJoe Thornber info->dirty = false;
144428e4698SJoe Thornber
1457a87edfeSJoe Thornber return 0;
1467a87edfeSJoe Thornber }
1477a87edfeSJoe Thornber
get_array_entry(struct dm_disk_bitset * info,dm_block_t root,uint32_t index,dm_block_t * new_root)1487a87edfeSJoe Thornber static int get_array_entry(struct dm_disk_bitset *info, dm_block_t root,
1497a87edfeSJoe Thornber uint32_t index, dm_block_t *new_root)
1507a87edfeSJoe Thornber {
1517a87edfeSJoe Thornber int r;
15286a3238cSHeinz Mauelshagen unsigned int array_index = index / BITS_PER_ARRAY_ENTRY;
1537a87edfeSJoe Thornber
1547a87edfeSJoe Thornber if (info->current_index_set) {
1557a87edfeSJoe Thornber if (info->current_index == array_index)
1567a87edfeSJoe Thornber return 0;
1577a87edfeSJoe Thornber
1587a87edfeSJoe Thornber r = dm_bitset_flush(info, root, new_root);
1597a87edfeSJoe Thornber if (r)
1607a87edfeSJoe Thornber return r;
1617a87edfeSJoe Thornber }
1627a87edfeSJoe Thornber
1637a87edfeSJoe Thornber return read_bits(info, root, array_index);
1647a87edfeSJoe Thornber }
1657a87edfeSJoe Thornber
dm_bitset_set_bit(struct dm_disk_bitset * info,dm_block_t root,uint32_t index,dm_block_t * new_root)1667a87edfeSJoe Thornber int dm_bitset_set_bit(struct dm_disk_bitset *info, dm_block_t root,
1677a87edfeSJoe Thornber uint32_t index, dm_block_t *new_root)
1687a87edfeSJoe Thornber {
1697a87edfeSJoe Thornber int r;
17086a3238cSHeinz Mauelshagen unsigned int b = index % BITS_PER_ARRAY_ENTRY;
1717a87edfeSJoe Thornber
1727a87edfeSJoe Thornber r = get_array_entry(info, root, index, new_root);
1737a87edfeSJoe Thornber if (r)
1747a87edfeSJoe Thornber return r;
1757a87edfeSJoe Thornber
1767a87edfeSJoe Thornber set_bit(b, (unsigned long *) &info->current_bits);
177428e4698SJoe Thornber info->dirty = true;
178428e4698SJoe Thornber
1797a87edfeSJoe Thornber return 0;
1807a87edfeSJoe Thornber }
1817a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_set_bit);
1827a87edfeSJoe Thornber
dm_bitset_clear_bit(struct dm_disk_bitset * info,dm_block_t root,uint32_t index,dm_block_t * new_root)1837a87edfeSJoe Thornber int dm_bitset_clear_bit(struct dm_disk_bitset *info, dm_block_t root,
1847a87edfeSJoe Thornber uint32_t index, dm_block_t *new_root)
1857a87edfeSJoe Thornber {
1867a87edfeSJoe Thornber int r;
18786a3238cSHeinz Mauelshagen unsigned int b = index % BITS_PER_ARRAY_ENTRY;
1887a87edfeSJoe Thornber
1897a87edfeSJoe Thornber r = get_array_entry(info, root, index, new_root);
1907a87edfeSJoe Thornber if (r)
1917a87edfeSJoe Thornber return r;
1927a87edfeSJoe Thornber
1937a87edfeSJoe Thornber clear_bit(b, (unsigned long *) &info->current_bits);
194428e4698SJoe Thornber info->dirty = true;
195428e4698SJoe Thornber
1967a87edfeSJoe Thornber return 0;
1977a87edfeSJoe Thornber }
1987a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_clear_bit);
1997a87edfeSJoe Thornber
dm_bitset_test_bit(struct dm_disk_bitset * info,dm_block_t root,uint32_t index,dm_block_t * new_root,bool * result)2007a87edfeSJoe Thornber int dm_bitset_test_bit(struct dm_disk_bitset *info, dm_block_t root,
2017a87edfeSJoe Thornber uint32_t index, dm_block_t *new_root, bool *result)
2027a87edfeSJoe Thornber {
2037a87edfeSJoe Thornber int r;
20486a3238cSHeinz Mauelshagen unsigned int b = index % BITS_PER_ARRAY_ENTRY;
2057a87edfeSJoe Thornber
2067a87edfeSJoe Thornber r = get_array_entry(info, root, index, new_root);
2077a87edfeSJoe Thornber if (r)
2087a87edfeSJoe Thornber return r;
2097a87edfeSJoe Thornber
2107a87edfeSJoe Thornber *result = test_bit(b, (unsigned long *) &info->current_bits);
2117a87edfeSJoe Thornber return 0;
2127a87edfeSJoe Thornber }
2137a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_test_bit);
2147a87edfeSJoe Thornber
cursor_next_array_entry(struct dm_bitset_cursor * c)2156fe28dbfSJoe Thornber static int cursor_next_array_entry(struct dm_bitset_cursor *c)
2166fe28dbfSJoe Thornber {
2176fe28dbfSJoe Thornber int r;
2186fe28dbfSJoe Thornber __le64 *value;
2196fe28dbfSJoe Thornber
2206fe28dbfSJoe Thornber r = dm_array_cursor_next(&c->cursor);
2216fe28dbfSJoe Thornber if (r)
2226fe28dbfSJoe Thornber return r;
2236fe28dbfSJoe Thornber
2246fe28dbfSJoe Thornber dm_array_cursor_get_value(&c->cursor, (void **) &value);
2256fe28dbfSJoe Thornber c->array_index++;
2266fe28dbfSJoe Thornber c->bit_index = 0;
2276fe28dbfSJoe Thornber c->current_bits = le64_to_cpu(*value);
2286fe28dbfSJoe Thornber return 0;
2296fe28dbfSJoe Thornber }
2306fe28dbfSJoe Thornber
dm_bitset_cursor_begin(struct dm_disk_bitset * info,dm_block_t root,uint32_t nr_entries,struct dm_bitset_cursor * c)2316fe28dbfSJoe Thornber int dm_bitset_cursor_begin(struct dm_disk_bitset *info,
2326fe28dbfSJoe Thornber dm_block_t root, uint32_t nr_entries,
2336fe28dbfSJoe Thornber struct dm_bitset_cursor *c)
2346fe28dbfSJoe Thornber {
2356fe28dbfSJoe Thornber int r;
2366fe28dbfSJoe Thornber __le64 *value;
2376fe28dbfSJoe Thornber
2386fe28dbfSJoe Thornber if (!nr_entries)
2396fe28dbfSJoe Thornber return -ENODATA;
2406fe28dbfSJoe Thornber
2416fe28dbfSJoe Thornber c->info = info;
2426fe28dbfSJoe Thornber c->entries_remaining = nr_entries;
2436fe28dbfSJoe Thornber
2446fe28dbfSJoe Thornber r = dm_array_cursor_begin(&info->array_info, root, &c->cursor);
2456fe28dbfSJoe Thornber if (r)
2466fe28dbfSJoe Thornber return r;
2476fe28dbfSJoe Thornber
2486fe28dbfSJoe Thornber dm_array_cursor_get_value(&c->cursor, (void **) &value);
2496fe28dbfSJoe Thornber c->array_index = 0;
2506fe28dbfSJoe Thornber c->bit_index = 0;
2516fe28dbfSJoe Thornber c->current_bits = le64_to_cpu(*value);
2526fe28dbfSJoe Thornber
2536fe28dbfSJoe Thornber return r;
2546fe28dbfSJoe Thornber }
2556fe28dbfSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_begin);
2566fe28dbfSJoe Thornber
dm_bitset_cursor_end(struct dm_bitset_cursor * c)2576fe28dbfSJoe Thornber void dm_bitset_cursor_end(struct dm_bitset_cursor *c)
2586fe28dbfSJoe Thornber {
2596fe28dbfSJoe Thornber return dm_array_cursor_end(&c->cursor);
2606fe28dbfSJoe Thornber }
2616fe28dbfSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_end);
2626fe28dbfSJoe Thornber
dm_bitset_cursor_next(struct dm_bitset_cursor * c)2636fe28dbfSJoe Thornber int dm_bitset_cursor_next(struct dm_bitset_cursor *c)
2646fe28dbfSJoe Thornber {
2656fe28dbfSJoe Thornber int r = 0;
2666fe28dbfSJoe Thornber
2676fe28dbfSJoe Thornber if (!c->entries_remaining)
2686fe28dbfSJoe Thornber return -ENODATA;
2696fe28dbfSJoe Thornber
2706fe28dbfSJoe Thornber c->entries_remaining--;
2716fe28dbfSJoe Thornber if (++c->bit_index > 63)
2726fe28dbfSJoe Thornber r = cursor_next_array_entry(c);
2736fe28dbfSJoe Thornber
2746fe28dbfSJoe Thornber return r;
2756fe28dbfSJoe Thornber }
2766fe28dbfSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_next);
2776fe28dbfSJoe Thornber
dm_bitset_cursor_skip(struct dm_bitset_cursor * c,uint32_t count)2789b696229SJoe Thornber int dm_bitset_cursor_skip(struct dm_bitset_cursor *c, uint32_t count)
2799b696229SJoe Thornber {
2809b696229SJoe Thornber int r;
2819b696229SJoe Thornber __le64 *value;
2829b696229SJoe Thornber uint32_t nr_array_skip;
2839b696229SJoe Thornber uint32_t remaining_in_word = 64 - c->bit_index;
2849b696229SJoe Thornber
2859b696229SJoe Thornber if (c->entries_remaining < count)
2869b696229SJoe Thornber return -ENODATA;
2879b696229SJoe Thornber
2889b696229SJoe Thornber if (count < remaining_in_word) {
2899b696229SJoe Thornber c->bit_index += count;
2909b696229SJoe Thornber c->entries_remaining -= count;
2919b696229SJoe Thornber return 0;
2929b696229SJoe Thornber
2939b696229SJoe Thornber } else {
2949b696229SJoe Thornber c->entries_remaining -= remaining_in_word;
2959b696229SJoe Thornber count -= remaining_in_word;
2969b696229SJoe Thornber }
2979b696229SJoe Thornber
2989b696229SJoe Thornber nr_array_skip = (count / 64) + 1;
2999b696229SJoe Thornber r = dm_array_cursor_skip(&c->cursor, nr_array_skip);
3009b696229SJoe Thornber if (r)
3019b696229SJoe Thornber return r;
3029b696229SJoe Thornber
3039b696229SJoe Thornber dm_array_cursor_get_value(&c->cursor, (void **) &value);
3049b696229SJoe Thornber c->entries_remaining -= count;
3059b696229SJoe Thornber c->array_index += nr_array_skip;
3069b696229SJoe Thornber c->bit_index = count & 63;
3079b696229SJoe Thornber c->current_bits = le64_to_cpu(*value);
3089b696229SJoe Thornber
3099b696229SJoe Thornber return 0;
3109b696229SJoe Thornber }
3119b696229SJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_skip);
3129b696229SJoe Thornber
dm_bitset_cursor_get_value(struct dm_bitset_cursor * c)3136fe28dbfSJoe Thornber bool dm_bitset_cursor_get_value(struct dm_bitset_cursor *c)
3146fe28dbfSJoe Thornber {
3156fe28dbfSJoe Thornber return test_bit(c->bit_index, (unsigned long *) &c->current_bits);
3166fe28dbfSJoe Thornber }
3176fe28dbfSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_get_value);
3186fe28dbfSJoe Thornber
3197a87edfeSJoe Thornber /*----------------------------------------------------------------*/
320