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 
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 
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;
45*86a3238cSHeinz Mauelshagen 	unsigned int nr_bits;
462151249eSJoe Thornber 	void *context;
472151249eSJoe Thornber };
482151249eSJoe Thornber 
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;
53*86a3238cSHeinz 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 
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;
772151249eSJoe Thornber 	p.fn = fn;
782151249eSJoe Thornber 	p.nr_bits = size;
792151249eSJoe Thornber 	p.context = context;
802151249eSJoe Thornber 
812151249eSJoe Thornber 	return dm_array_new(&info->array_info, root, dm_div_up(size, 64), pack_bits, &p);
822151249eSJoe Thornber }
832151249eSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_new);
842151249eSJoe Thornber 
857a87edfeSJoe Thornber int dm_bitset_resize(struct dm_disk_bitset *info, dm_block_t root,
867a87edfeSJoe Thornber 		     uint32_t old_nr_entries, uint32_t new_nr_entries,
877a87edfeSJoe Thornber 		     bool default_value, dm_block_t *new_root)
887a87edfeSJoe Thornber {
897a87edfeSJoe Thornber 	uint32_t old_blocks = dm_div_up(old_nr_entries, BITS_PER_ARRAY_ENTRY);
907a87edfeSJoe Thornber 	uint32_t new_blocks = dm_div_up(new_nr_entries, BITS_PER_ARRAY_ENTRY);
917a87edfeSJoe Thornber 	__le64 value = default_value ? cpu_to_le64(~0) : cpu_to_le64(0);
927a87edfeSJoe Thornber 
937a87edfeSJoe Thornber 	__dm_bless_for_disk(&value);
947a87edfeSJoe Thornber 	return dm_array_resize(&info->array_info, root, old_blocks, new_blocks,
957a87edfeSJoe Thornber 			       &value, new_root);
967a87edfeSJoe Thornber }
977a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_resize);
987a87edfeSJoe Thornber 
997a87edfeSJoe Thornber int dm_bitset_del(struct dm_disk_bitset *info, dm_block_t root)
1007a87edfeSJoe Thornber {
1017a87edfeSJoe Thornber 	return dm_array_del(&info->array_info, root);
1027a87edfeSJoe Thornber }
1037a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_del);
1047a87edfeSJoe Thornber 
1057a87edfeSJoe Thornber int dm_bitset_flush(struct dm_disk_bitset *info, dm_block_t root,
1067a87edfeSJoe Thornber 		    dm_block_t *new_root)
1077a87edfeSJoe Thornber {
1087a87edfeSJoe Thornber 	int r;
1097a87edfeSJoe Thornber 	__le64 value;
1107a87edfeSJoe Thornber 
111428e4698SJoe Thornber 	if (!info->current_index_set || !info->dirty)
1127a87edfeSJoe Thornber 		return 0;
1137a87edfeSJoe Thornber 
1147a87edfeSJoe Thornber 	value = cpu_to_le64(info->current_bits);
1157a87edfeSJoe Thornber 
1167a87edfeSJoe Thornber 	__dm_bless_for_disk(&value);
1177a87edfeSJoe Thornber 	r = dm_array_set_value(&info->array_info, root, info->current_index,
1187a87edfeSJoe Thornber 			       &value, new_root);
1197a87edfeSJoe Thornber 	if (r)
1207a87edfeSJoe Thornber 		return r;
1217a87edfeSJoe Thornber 
1227a87edfeSJoe Thornber 	info->current_index_set = false;
123428e4698SJoe Thornber 	info->dirty = false;
124428e4698SJoe Thornber 
1257a87edfeSJoe Thornber 	return 0;
1267a87edfeSJoe Thornber }
1277a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_flush);
1287a87edfeSJoe Thornber 
1297a87edfeSJoe Thornber static int read_bits(struct dm_disk_bitset *info, dm_block_t root,
1307a87edfeSJoe Thornber 		     uint32_t array_index)
1317a87edfeSJoe Thornber {
1327a87edfeSJoe Thornber 	int r;
1337a87edfeSJoe Thornber 	__le64 value;
1347a87edfeSJoe Thornber 
1357a87edfeSJoe Thornber 	r = dm_array_get_value(&info->array_info, root, array_index, &value);
1367a87edfeSJoe Thornber 	if (r)
1377a87edfeSJoe Thornber 		return r;
1387a87edfeSJoe Thornber 
1397a87edfeSJoe Thornber 	info->current_bits = le64_to_cpu(value);
1407a87edfeSJoe Thornber 	info->current_index_set = true;
1417a87edfeSJoe Thornber 	info->current_index = array_index;
142428e4698SJoe Thornber 	info->dirty = false;
143428e4698SJoe Thornber 
1447a87edfeSJoe Thornber 	return 0;
1457a87edfeSJoe Thornber }
1467a87edfeSJoe Thornber 
1477a87edfeSJoe Thornber static int get_array_entry(struct dm_disk_bitset *info, dm_block_t root,
1487a87edfeSJoe Thornber 			   uint32_t index, dm_block_t *new_root)
1497a87edfeSJoe Thornber {
1507a87edfeSJoe Thornber 	int r;
151*86a3238cSHeinz Mauelshagen 	unsigned int array_index = index / BITS_PER_ARRAY_ENTRY;
1527a87edfeSJoe Thornber 
1537a87edfeSJoe Thornber 	if (info->current_index_set) {
1547a87edfeSJoe Thornber 		if (info->current_index == array_index)
1557a87edfeSJoe Thornber 			return 0;
1567a87edfeSJoe Thornber 
1577a87edfeSJoe Thornber 		r = dm_bitset_flush(info, root, new_root);
1587a87edfeSJoe Thornber 		if (r)
1597a87edfeSJoe Thornber 			return r;
1607a87edfeSJoe Thornber 	}
1617a87edfeSJoe Thornber 
1627a87edfeSJoe Thornber 	return read_bits(info, root, array_index);
1637a87edfeSJoe Thornber }
1647a87edfeSJoe Thornber 
1657a87edfeSJoe Thornber int dm_bitset_set_bit(struct dm_disk_bitset *info, dm_block_t root,
1667a87edfeSJoe Thornber 		      uint32_t index, dm_block_t *new_root)
1677a87edfeSJoe Thornber {
1687a87edfeSJoe Thornber 	int r;
169*86a3238cSHeinz Mauelshagen 	unsigned int b = index % BITS_PER_ARRAY_ENTRY;
1707a87edfeSJoe Thornber 
1717a87edfeSJoe Thornber 	r = get_array_entry(info, root, index, new_root);
1727a87edfeSJoe Thornber 	if (r)
1737a87edfeSJoe Thornber 		return r;
1747a87edfeSJoe Thornber 
1757a87edfeSJoe Thornber 	set_bit(b, (unsigned long *) &info->current_bits);
176428e4698SJoe Thornber 	info->dirty = true;
177428e4698SJoe Thornber 
1787a87edfeSJoe Thornber 	return 0;
1797a87edfeSJoe Thornber }
1807a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_set_bit);
1817a87edfeSJoe Thornber 
1827a87edfeSJoe Thornber int dm_bitset_clear_bit(struct dm_disk_bitset *info, dm_block_t root,
1837a87edfeSJoe Thornber 			uint32_t index, dm_block_t *new_root)
1847a87edfeSJoe Thornber {
1857a87edfeSJoe Thornber 	int r;
186*86a3238cSHeinz Mauelshagen 	unsigned int b = index % BITS_PER_ARRAY_ENTRY;
1877a87edfeSJoe Thornber 
1887a87edfeSJoe Thornber 	r = get_array_entry(info, root, index, new_root);
1897a87edfeSJoe Thornber 	if (r)
1907a87edfeSJoe Thornber 		return r;
1917a87edfeSJoe Thornber 
1927a87edfeSJoe Thornber 	clear_bit(b, (unsigned long *) &info->current_bits);
193428e4698SJoe Thornber 	info->dirty = true;
194428e4698SJoe Thornber 
1957a87edfeSJoe Thornber 	return 0;
1967a87edfeSJoe Thornber }
1977a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_clear_bit);
1987a87edfeSJoe Thornber 
1997a87edfeSJoe Thornber int dm_bitset_test_bit(struct dm_disk_bitset *info, dm_block_t root,
2007a87edfeSJoe Thornber 		       uint32_t index, dm_block_t *new_root, bool *result)
2017a87edfeSJoe Thornber {
2027a87edfeSJoe Thornber 	int r;
203*86a3238cSHeinz Mauelshagen 	unsigned int b = index % BITS_PER_ARRAY_ENTRY;
2047a87edfeSJoe Thornber 
2057a87edfeSJoe Thornber 	r = get_array_entry(info, root, index, new_root);
2067a87edfeSJoe Thornber 	if (r)
2077a87edfeSJoe Thornber 		return r;
2087a87edfeSJoe Thornber 
2097a87edfeSJoe Thornber 	*result = test_bit(b, (unsigned long *) &info->current_bits);
2107a87edfeSJoe Thornber 	return 0;
2117a87edfeSJoe Thornber }
2127a87edfeSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_test_bit);
2137a87edfeSJoe Thornber 
2146fe28dbfSJoe Thornber static int cursor_next_array_entry(struct dm_bitset_cursor *c)
2156fe28dbfSJoe Thornber {
2166fe28dbfSJoe Thornber 	int r;
2176fe28dbfSJoe Thornber 	__le64 *value;
2186fe28dbfSJoe Thornber 
2196fe28dbfSJoe Thornber 	r = dm_array_cursor_next(&c->cursor);
2206fe28dbfSJoe Thornber 	if (r)
2216fe28dbfSJoe Thornber 		return r;
2226fe28dbfSJoe Thornber 
2236fe28dbfSJoe Thornber 	dm_array_cursor_get_value(&c->cursor, (void **) &value);
2246fe28dbfSJoe Thornber 	c->array_index++;
2256fe28dbfSJoe Thornber 	c->bit_index = 0;
2266fe28dbfSJoe Thornber 	c->current_bits = le64_to_cpu(*value);
2276fe28dbfSJoe Thornber 	return 0;
2286fe28dbfSJoe Thornber }
2296fe28dbfSJoe Thornber 
2306fe28dbfSJoe Thornber int dm_bitset_cursor_begin(struct dm_disk_bitset *info,
2316fe28dbfSJoe Thornber 			   dm_block_t root, uint32_t nr_entries,
2326fe28dbfSJoe Thornber 			   struct dm_bitset_cursor *c)
2336fe28dbfSJoe Thornber {
2346fe28dbfSJoe Thornber 	int r;
2356fe28dbfSJoe Thornber 	__le64 *value;
2366fe28dbfSJoe Thornber 
2376fe28dbfSJoe Thornber 	if (!nr_entries)
2386fe28dbfSJoe Thornber 		return -ENODATA;
2396fe28dbfSJoe Thornber 
2406fe28dbfSJoe Thornber 	c->info = info;
2416fe28dbfSJoe Thornber 	c->entries_remaining = nr_entries;
2426fe28dbfSJoe Thornber 
2436fe28dbfSJoe Thornber 	r = dm_array_cursor_begin(&info->array_info, root, &c->cursor);
2446fe28dbfSJoe Thornber 	if (r)
2456fe28dbfSJoe Thornber 		return r;
2466fe28dbfSJoe Thornber 
2476fe28dbfSJoe Thornber 	dm_array_cursor_get_value(&c->cursor, (void **) &value);
2486fe28dbfSJoe Thornber 	c->array_index = 0;
2496fe28dbfSJoe Thornber 	c->bit_index = 0;
2506fe28dbfSJoe Thornber 	c->current_bits = le64_to_cpu(*value);
2516fe28dbfSJoe Thornber 
2526fe28dbfSJoe Thornber 	return r;
2536fe28dbfSJoe Thornber }
2546fe28dbfSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_begin);
2556fe28dbfSJoe Thornber 
2566fe28dbfSJoe Thornber void dm_bitset_cursor_end(struct dm_bitset_cursor *c)
2576fe28dbfSJoe Thornber {
2586fe28dbfSJoe Thornber 	return dm_array_cursor_end(&c->cursor);
2596fe28dbfSJoe Thornber }
2606fe28dbfSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_end);
2616fe28dbfSJoe Thornber 
2626fe28dbfSJoe Thornber int dm_bitset_cursor_next(struct dm_bitset_cursor *c)
2636fe28dbfSJoe Thornber {
2646fe28dbfSJoe Thornber 	int r = 0;
2656fe28dbfSJoe Thornber 
2666fe28dbfSJoe Thornber 	if (!c->entries_remaining)
2676fe28dbfSJoe Thornber 		return -ENODATA;
2686fe28dbfSJoe Thornber 
2696fe28dbfSJoe Thornber 	c->entries_remaining--;
2706fe28dbfSJoe Thornber 	if (++c->bit_index > 63)
2716fe28dbfSJoe Thornber 		r = cursor_next_array_entry(c);
2726fe28dbfSJoe Thornber 
2736fe28dbfSJoe Thornber 	return r;
2746fe28dbfSJoe Thornber }
2756fe28dbfSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_next);
2766fe28dbfSJoe Thornber 
2779b696229SJoe Thornber int dm_bitset_cursor_skip(struct dm_bitset_cursor *c, uint32_t count)
2789b696229SJoe Thornber {
2799b696229SJoe Thornber 	int r;
2809b696229SJoe Thornber 	__le64 *value;
2819b696229SJoe Thornber 	uint32_t nr_array_skip;
2829b696229SJoe Thornber 	uint32_t remaining_in_word = 64 - c->bit_index;
2839b696229SJoe Thornber 
2849b696229SJoe Thornber 	if (c->entries_remaining < count)
2859b696229SJoe Thornber 		return -ENODATA;
2869b696229SJoe Thornber 
2879b696229SJoe Thornber 	if (count < remaining_in_word) {
2889b696229SJoe Thornber 		c->bit_index += count;
2899b696229SJoe Thornber 		c->entries_remaining -= count;
2909b696229SJoe Thornber 		return 0;
2919b696229SJoe Thornber 
2929b696229SJoe Thornber 	} else {
2939b696229SJoe Thornber 		c->entries_remaining -= remaining_in_word;
2949b696229SJoe Thornber 		count -= remaining_in_word;
2959b696229SJoe Thornber 	}
2969b696229SJoe Thornber 
2979b696229SJoe Thornber 	nr_array_skip = (count / 64) + 1;
2989b696229SJoe Thornber 	r = dm_array_cursor_skip(&c->cursor, nr_array_skip);
2999b696229SJoe Thornber 	if (r)
3009b696229SJoe Thornber 		return r;
3019b696229SJoe Thornber 
3029b696229SJoe Thornber 	dm_array_cursor_get_value(&c->cursor, (void **) &value);
3039b696229SJoe Thornber 	c->entries_remaining -= count;
3049b696229SJoe Thornber 	c->array_index += nr_array_skip;
3059b696229SJoe Thornber 	c->bit_index = count & 63;
3069b696229SJoe Thornber 	c->current_bits = le64_to_cpu(*value);
3079b696229SJoe Thornber 
3089b696229SJoe Thornber 	return 0;
3099b696229SJoe Thornber }
3109b696229SJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_skip);
3119b696229SJoe Thornber 
3126fe28dbfSJoe Thornber bool dm_bitset_cursor_get_value(struct dm_bitset_cursor *c)
3136fe28dbfSJoe Thornber {
3146fe28dbfSJoe Thornber 	return test_bit(c->bit_index, (unsigned long *) &c->current_bits);
3156fe28dbfSJoe Thornber }
3166fe28dbfSJoe Thornber EXPORT_SYMBOL_GPL(dm_bitset_cursor_get_value);
3176fe28dbfSJoe Thornber 
3187a87edfeSJoe Thornber /*----------------------------------------------------------------*/
319