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