13bd94003SHeinz Mauelshagen // SPDX-License-Identifier: GPL-2.0-only
23241b1d3SJoe Thornber /*
33241b1d3SJoe Thornber  * Copyright (C) 2011 Red Hat, Inc.
43241b1d3SJoe Thornber  *
53241b1d3SJoe Thornber  * This file is released under the GPL.
63241b1d3SJoe Thornber  */
73241b1d3SJoe Thornber #include "dm-block-manager.h"
83241b1d3SJoe Thornber #include "dm-persistent-data-internal.h"
93241b1d3SJoe Thornber 
10afa53df8SMikulas Patocka #include <linux/dm-bufio.h>
113241b1d3SJoe Thornber #include <linux/crc32c.h>
123241b1d3SJoe Thornber #include <linux/module.h>
133241b1d3SJoe Thornber #include <linux/slab.h>
143241b1d3SJoe Thornber #include <linux/rwsem.h>
153241b1d3SJoe Thornber #include <linux/device-mapper.h>
163241b1d3SJoe Thornber #include <linux/stacktrace.h>
170881e7bdSIngo Molnar #include <linux/sched/task.h>
183241b1d3SJoe Thornber 
193241b1d3SJoe Thornber #define DM_MSG_PREFIX "block manager"
203241b1d3SJoe Thornber 
213241b1d3SJoe Thornber /*----------------------------------------------------------------*/
223241b1d3SJoe Thornber 
232e8ed711SJoe Thornber #ifdef CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING
242e8ed711SJoe Thornber 
253241b1d3SJoe Thornber /*
263241b1d3SJoe Thornber  * This is a read/write semaphore with a couple of differences.
273241b1d3SJoe Thornber  *
283241b1d3SJoe Thornber  * i) There is a restriction on the number of concurrent read locks that
293241b1d3SJoe Thornber  * may be held at once.  This is just an implementation detail.
303241b1d3SJoe Thornber  *
313241b1d3SJoe Thornber  * ii) Recursive locking attempts are detected and return EINVAL.  A stack
3283f0d77aSMasanari Iida  * trace is also emitted for the previous lock acquisition.
333241b1d3SJoe Thornber  *
343241b1d3SJoe Thornber  * iii) Priority is given to write locks.
353241b1d3SJoe Thornber  */
363241b1d3SJoe Thornber #define MAX_HOLDERS 4
373241b1d3SJoe Thornber #define MAX_STACK 10
383241b1d3SJoe Thornber 
39be9c52edSThomas Gleixner struct stack_store {
40be9c52edSThomas Gleixner 	unsigned int	nr_entries;
41be9c52edSThomas Gleixner 	unsigned long	entries[MAX_STACK];
42be9c52edSThomas Gleixner };
433241b1d3SJoe Thornber 
443241b1d3SJoe Thornber struct block_lock {
453241b1d3SJoe Thornber 	spinlock_t lock;
463241b1d3SJoe Thornber 	__s32 count;
473241b1d3SJoe Thornber 	struct list_head waiters;
483241b1d3SJoe Thornber 	struct task_struct *holders[MAX_HOLDERS];
493241b1d3SJoe Thornber 
503241b1d3SJoe Thornber #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
51be9c52edSThomas Gleixner 	struct stack_store traces[MAX_HOLDERS];
523241b1d3SJoe Thornber #endif
533241b1d3SJoe Thornber };
543241b1d3SJoe Thornber 
553241b1d3SJoe Thornber struct waiter {
563241b1d3SJoe Thornber 	struct list_head list;
573241b1d3SJoe Thornber 	struct task_struct *task;
583241b1d3SJoe Thornber 	int wants_write;
593241b1d3SJoe Thornber };
603241b1d3SJoe Thornber 
__find_holder(struct block_lock * lock,struct task_struct * task)6186a3238cSHeinz Mauelshagen static unsigned int __find_holder(struct block_lock *lock,
623241b1d3SJoe Thornber 			      struct task_struct *task)
633241b1d3SJoe Thornber {
6486a3238cSHeinz Mauelshagen 	unsigned int i;
653241b1d3SJoe Thornber 
663241b1d3SJoe Thornber 	for (i = 0; i < MAX_HOLDERS; i++)
673241b1d3SJoe Thornber 		if (lock->holders[i] == task)
683241b1d3SJoe Thornber 			break;
693241b1d3SJoe Thornber 
703241b1d3SJoe Thornber 	BUG_ON(i == MAX_HOLDERS);
713241b1d3SJoe Thornber 	return i;
723241b1d3SJoe Thornber }
733241b1d3SJoe Thornber 
743241b1d3SJoe Thornber /* call this *after* you increment lock->count */
__add_holder(struct block_lock * lock,struct task_struct * task)753241b1d3SJoe Thornber static void __add_holder(struct block_lock *lock, struct task_struct *task)
763241b1d3SJoe Thornber {
7786a3238cSHeinz Mauelshagen 	unsigned int h = __find_holder(lock, NULL);
783241b1d3SJoe Thornber #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
79be9c52edSThomas Gleixner 	struct stack_store *t;
803241b1d3SJoe Thornber #endif
813241b1d3SJoe Thornber 
823241b1d3SJoe Thornber 	get_task_struct(task);
833241b1d3SJoe Thornber 	lock->holders[h] = task;
843241b1d3SJoe Thornber 
853241b1d3SJoe Thornber #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
863241b1d3SJoe Thornber 	t = lock->traces + h;
87be9c52edSThomas Gleixner 	t->nr_entries = stack_trace_save(t->entries, MAX_STACK, 2);
883241b1d3SJoe Thornber #endif
893241b1d3SJoe Thornber }
903241b1d3SJoe Thornber 
913241b1d3SJoe Thornber /* call this *before* you decrement lock->count */
__del_holder(struct block_lock * lock,struct task_struct * task)923241b1d3SJoe Thornber static void __del_holder(struct block_lock *lock, struct task_struct *task)
933241b1d3SJoe Thornber {
9486a3238cSHeinz Mauelshagen 	unsigned int h = __find_holder(lock, task);
950ef0b471SHeinz Mauelshagen 
963241b1d3SJoe Thornber 	lock->holders[h] = NULL;
973241b1d3SJoe Thornber 	put_task_struct(task);
983241b1d3SJoe Thornber }
993241b1d3SJoe Thornber 
__check_holder(struct block_lock * lock)1003241b1d3SJoe Thornber static int __check_holder(struct block_lock *lock)
1013241b1d3SJoe Thornber {
10286a3238cSHeinz Mauelshagen 	unsigned int i;
1033241b1d3SJoe Thornber 
1043241b1d3SJoe Thornber 	for (i = 0; i < MAX_HOLDERS; i++) {
1053241b1d3SJoe Thornber 		if (lock->holders[i] == current) {
10610343180SMike Snitzer 			DMERR("recursive lock detected in metadata");
1073241b1d3SJoe Thornber #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
1083241b1d3SJoe Thornber 			DMERR("previously held here:");
109be9c52edSThomas Gleixner 			stack_trace_print(lock->traces[i].entries,
110be9c52edSThomas Gleixner 					  lock->traces[i].nr_entries, 4);
1113241b1d3SJoe Thornber 
11283f0d77aSMasanari Iida 			DMERR("subsequent acquisition attempted here:");
113313c9b97SMikulas Patocka 			dump_stack();
1143241b1d3SJoe Thornber #endif
1153241b1d3SJoe Thornber 			return -EINVAL;
1163241b1d3SJoe Thornber 		}
1173241b1d3SJoe Thornber 	}
1183241b1d3SJoe Thornber 
1193241b1d3SJoe Thornber 	return 0;
1203241b1d3SJoe Thornber }
1213241b1d3SJoe Thornber 
__wait(struct waiter * w)1223241b1d3SJoe Thornber static void __wait(struct waiter *w)
1233241b1d3SJoe Thornber {
1243241b1d3SJoe Thornber 	for (;;) {
125642fa448SDavidlohr Bueso 		set_current_state(TASK_UNINTERRUPTIBLE);
1263241b1d3SJoe Thornber 
1273241b1d3SJoe Thornber 		if (!w->task)
1283241b1d3SJoe Thornber 			break;
1293241b1d3SJoe Thornber 
1303241b1d3SJoe Thornber 		schedule();
1313241b1d3SJoe Thornber 	}
1323241b1d3SJoe Thornber 
133642fa448SDavidlohr Bueso 	set_current_state(TASK_RUNNING);
1343241b1d3SJoe Thornber }
1353241b1d3SJoe Thornber 
__wake_waiter(struct waiter * w)1363241b1d3SJoe Thornber static void __wake_waiter(struct waiter *w)
1373241b1d3SJoe Thornber {
1383241b1d3SJoe Thornber 	struct task_struct *task;
1393241b1d3SJoe Thornber 
1403241b1d3SJoe Thornber 	list_del(&w->list);
1413241b1d3SJoe Thornber 	task = w->task;
1423241b1d3SJoe Thornber 	smp_mb();
1433241b1d3SJoe Thornber 	w->task = NULL;
1443241b1d3SJoe Thornber 	wake_up_process(task);
1453241b1d3SJoe Thornber }
1463241b1d3SJoe Thornber 
1473241b1d3SJoe Thornber /*
1483241b1d3SJoe Thornber  * We either wake a few readers or a single writer.
1493241b1d3SJoe Thornber  */
__wake_many(struct block_lock * lock)1503241b1d3SJoe Thornber static void __wake_many(struct block_lock *lock)
1513241b1d3SJoe Thornber {
1523241b1d3SJoe Thornber 	struct waiter *w, *tmp;
1533241b1d3SJoe Thornber 
1543241b1d3SJoe Thornber 	BUG_ON(lock->count < 0);
1553241b1d3SJoe Thornber 	list_for_each_entry_safe(w, tmp, &lock->waiters, list) {
1563241b1d3SJoe Thornber 		if (lock->count >= MAX_HOLDERS)
1573241b1d3SJoe Thornber 			return;
1583241b1d3SJoe Thornber 
1593241b1d3SJoe Thornber 		if (w->wants_write) {
1603241b1d3SJoe Thornber 			if (lock->count > 0)
1613241b1d3SJoe Thornber 				return; /* still read locked */
1623241b1d3SJoe Thornber 
1633241b1d3SJoe Thornber 			lock->count = -1;
1643241b1d3SJoe Thornber 			__add_holder(lock, w->task);
1653241b1d3SJoe Thornber 			__wake_waiter(w);
1663241b1d3SJoe Thornber 			return;
1673241b1d3SJoe Thornber 		}
1683241b1d3SJoe Thornber 
1693241b1d3SJoe Thornber 		lock->count++;
1703241b1d3SJoe Thornber 		__add_holder(lock, w->task);
1713241b1d3SJoe Thornber 		__wake_waiter(w);
1723241b1d3SJoe Thornber 	}
1733241b1d3SJoe Thornber }
1743241b1d3SJoe Thornber 
bl_init(struct block_lock * lock)1753241b1d3SJoe Thornber static void bl_init(struct block_lock *lock)
1763241b1d3SJoe Thornber {
1773241b1d3SJoe Thornber 	int i;
1783241b1d3SJoe Thornber 
1793241b1d3SJoe Thornber 	spin_lock_init(&lock->lock);
1803241b1d3SJoe Thornber 	lock->count = 0;
1813241b1d3SJoe Thornber 	INIT_LIST_HEAD(&lock->waiters);
1823241b1d3SJoe Thornber 	for (i = 0; i < MAX_HOLDERS; i++)
1833241b1d3SJoe Thornber 		lock->holders[i] = NULL;
1843241b1d3SJoe Thornber }
1853241b1d3SJoe Thornber 
__available_for_read(struct block_lock * lock)1863241b1d3SJoe Thornber static int __available_for_read(struct block_lock *lock)
1873241b1d3SJoe Thornber {
1883241b1d3SJoe Thornber 	return lock->count >= 0 &&
1893241b1d3SJoe Thornber 		lock->count < MAX_HOLDERS &&
1903241b1d3SJoe Thornber 		list_empty(&lock->waiters);
1913241b1d3SJoe Thornber }
1923241b1d3SJoe Thornber 
bl_down_read(struct block_lock * lock)1933241b1d3SJoe Thornber static int bl_down_read(struct block_lock *lock)
1943241b1d3SJoe Thornber {
1953241b1d3SJoe Thornber 	int r;
1963241b1d3SJoe Thornber 	struct waiter w;
1973241b1d3SJoe Thornber 
1983241b1d3SJoe Thornber 	spin_lock(&lock->lock);
1993241b1d3SJoe Thornber 	r = __check_holder(lock);
2003241b1d3SJoe Thornber 	if (r) {
2013241b1d3SJoe Thornber 		spin_unlock(&lock->lock);
2023241b1d3SJoe Thornber 		return r;
2033241b1d3SJoe Thornber 	}
2043241b1d3SJoe Thornber 
2053241b1d3SJoe Thornber 	if (__available_for_read(lock)) {
2063241b1d3SJoe Thornber 		lock->count++;
2073241b1d3SJoe Thornber 		__add_holder(lock, current);
2083241b1d3SJoe Thornber 		spin_unlock(&lock->lock);
2093241b1d3SJoe Thornber 		return 0;
2103241b1d3SJoe Thornber 	}
2113241b1d3SJoe Thornber 
2123241b1d3SJoe Thornber 	get_task_struct(current);
2133241b1d3SJoe Thornber 
2143241b1d3SJoe Thornber 	w.task = current;
2153241b1d3SJoe Thornber 	w.wants_write = 0;
2163241b1d3SJoe Thornber 	list_add_tail(&w.list, &lock->waiters);
2173241b1d3SJoe Thornber 	spin_unlock(&lock->lock);
2183241b1d3SJoe Thornber 
2193241b1d3SJoe Thornber 	__wait(&w);
2203241b1d3SJoe Thornber 	put_task_struct(current);
2213241b1d3SJoe Thornber 	return 0;
2223241b1d3SJoe Thornber }
2233241b1d3SJoe Thornber 
bl_down_read_nonblock(struct block_lock * lock)2243241b1d3SJoe Thornber static int bl_down_read_nonblock(struct block_lock *lock)
2253241b1d3SJoe Thornber {
2263241b1d3SJoe Thornber 	int r;
2273241b1d3SJoe Thornber 
2283241b1d3SJoe Thornber 	spin_lock(&lock->lock);
2293241b1d3SJoe Thornber 	r = __check_holder(lock);
2303241b1d3SJoe Thornber 	if (r)
2313241b1d3SJoe Thornber 		goto out;
2323241b1d3SJoe Thornber 
2333241b1d3SJoe Thornber 	if (__available_for_read(lock)) {
2343241b1d3SJoe Thornber 		lock->count++;
2353241b1d3SJoe Thornber 		__add_holder(lock, current);
2363241b1d3SJoe Thornber 		r = 0;
2373241b1d3SJoe Thornber 	} else
2383241b1d3SJoe Thornber 		r = -EWOULDBLOCK;
2393241b1d3SJoe Thornber 
2403241b1d3SJoe Thornber out:
2413241b1d3SJoe Thornber 	spin_unlock(&lock->lock);
2423241b1d3SJoe Thornber 	return r;
2433241b1d3SJoe Thornber }
2443241b1d3SJoe Thornber 
bl_up_read(struct block_lock * lock)2453241b1d3SJoe Thornber static void bl_up_read(struct block_lock *lock)
2463241b1d3SJoe Thornber {
2473241b1d3SJoe Thornber 	spin_lock(&lock->lock);
2483241b1d3SJoe Thornber 	BUG_ON(lock->count <= 0);
2493241b1d3SJoe Thornber 	__del_holder(lock, current);
2503241b1d3SJoe Thornber 	--lock->count;
2513241b1d3SJoe Thornber 	if (!list_empty(&lock->waiters))
2523241b1d3SJoe Thornber 		__wake_many(lock);
2533241b1d3SJoe Thornber 	spin_unlock(&lock->lock);
2543241b1d3SJoe Thornber }
2553241b1d3SJoe Thornber 
bl_down_write(struct block_lock * lock)2563241b1d3SJoe Thornber static int bl_down_write(struct block_lock *lock)
2573241b1d3SJoe Thornber {
2583241b1d3SJoe Thornber 	int r;
2593241b1d3SJoe Thornber 	struct waiter w;
2603241b1d3SJoe Thornber 
2613241b1d3SJoe Thornber 	spin_lock(&lock->lock);
2623241b1d3SJoe Thornber 	r = __check_holder(lock);
2633241b1d3SJoe Thornber 	if (r) {
2643241b1d3SJoe Thornber 		spin_unlock(&lock->lock);
2653241b1d3SJoe Thornber 		return r;
2663241b1d3SJoe Thornber 	}
2673241b1d3SJoe Thornber 
2683241b1d3SJoe Thornber 	if (lock->count == 0 && list_empty(&lock->waiters)) {
2693241b1d3SJoe Thornber 		lock->count = -1;
2703241b1d3SJoe Thornber 		__add_holder(lock, current);
2713241b1d3SJoe Thornber 		spin_unlock(&lock->lock);
2723241b1d3SJoe Thornber 		return 0;
2733241b1d3SJoe Thornber 	}
2743241b1d3SJoe Thornber 
2753241b1d3SJoe Thornber 	get_task_struct(current);
2763241b1d3SJoe Thornber 	w.task = current;
2773241b1d3SJoe Thornber 	w.wants_write = 1;
2783241b1d3SJoe Thornber 
2793241b1d3SJoe Thornber 	/*
2803241b1d3SJoe Thornber 	 * Writers given priority. We know there's only one mutator in the
2813241b1d3SJoe Thornber 	 * system, so ignoring the ordering reversal.
2823241b1d3SJoe Thornber 	 */
2833241b1d3SJoe Thornber 	list_add(&w.list, &lock->waiters);
2843241b1d3SJoe Thornber 	spin_unlock(&lock->lock);
2853241b1d3SJoe Thornber 
2863241b1d3SJoe Thornber 	__wait(&w);
2873241b1d3SJoe Thornber 	put_task_struct(current);
2883241b1d3SJoe Thornber 
2893241b1d3SJoe Thornber 	return 0;
2903241b1d3SJoe Thornber }
2913241b1d3SJoe Thornber 
bl_up_write(struct block_lock * lock)2923241b1d3SJoe Thornber static void bl_up_write(struct block_lock *lock)
2933241b1d3SJoe Thornber {
2943241b1d3SJoe Thornber 	spin_lock(&lock->lock);
2953241b1d3SJoe Thornber 	__del_holder(lock, current);
2963241b1d3SJoe Thornber 	lock->count = 0;
2973241b1d3SJoe Thornber 	if (!list_empty(&lock->waiters))
2983241b1d3SJoe Thornber 		__wake_many(lock);
2993241b1d3SJoe Thornber 	spin_unlock(&lock->lock);
3003241b1d3SJoe Thornber }
3013241b1d3SJoe Thornber 
report_recursive_bug(dm_block_t b,int r)3023241b1d3SJoe Thornber static void report_recursive_bug(dm_block_t b, int r)
3033241b1d3SJoe Thornber {
3043241b1d3SJoe Thornber 	if (r == -EINVAL)
3053241b1d3SJoe Thornber 		DMERR("recursive acquisition of block %llu requested.",
3063241b1d3SJoe Thornber 		      (unsigned long long) b);
3073241b1d3SJoe Thornber }
3083241b1d3SJoe Thornber 
3092e8ed711SJoe Thornber #else  /* !CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING */
3102e8ed711SJoe Thornber 
3112e8ed711SJoe Thornber #define bl_init(x) do { } while (0)
3122e8ed711SJoe Thornber #define bl_down_read(x) 0
3132e8ed711SJoe Thornber #define bl_down_read_nonblock(x) 0
3142e8ed711SJoe Thornber #define bl_up_read(x) do { } while (0)
3152e8ed711SJoe Thornber #define bl_down_write(x) 0
3162e8ed711SJoe Thornber #define bl_up_write(x) do { } while (0)
3172e8ed711SJoe Thornber #define report_recursive_bug(x, y) do { } while (0)
3182e8ed711SJoe Thornber 
3192e8ed711SJoe Thornber #endif /* CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING */
3202e8ed711SJoe Thornber 
3213241b1d3SJoe Thornber /*----------------------------------------------------------------*/
3223241b1d3SJoe Thornber 
3233241b1d3SJoe Thornber /*
3243241b1d3SJoe Thornber  * Block manager is currently implemented using dm-bufio.  struct
3253241b1d3SJoe Thornber  * dm_block_manager and struct dm_block map directly onto a couple of
3263241b1d3SJoe Thornber  * structs in the bufio interface.  I want to retain the freedom to move
3273241b1d3SJoe Thornber  * away from bufio in the future.  So these structs are just cast within
3283241b1d3SJoe Thornber  * this .c file, rather than making it through to the public interface.
3293241b1d3SJoe Thornber  */
to_buffer(struct dm_block * b)3303241b1d3SJoe Thornber static struct dm_buffer *to_buffer(struct dm_block *b)
3313241b1d3SJoe Thornber {
3323241b1d3SJoe Thornber 	return (struct dm_buffer *) b;
3333241b1d3SJoe Thornber }
3343241b1d3SJoe Thornber 
dm_block_location(struct dm_block * b)3353241b1d3SJoe Thornber dm_block_t dm_block_location(struct dm_block *b)
3363241b1d3SJoe Thornber {
3373241b1d3SJoe Thornber 	return dm_bufio_get_block_number(to_buffer(b));
3383241b1d3SJoe Thornber }
3393241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_block_location);
3403241b1d3SJoe Thornber 
dm_block_data(struct dm_block * b)3413241b1d3SJoe Thornber void *dm_block_data(struct dm_block *b)
3423241b1d3SJoe Thornber {
3433241b1d3SJoe Thornber 	return dm_bufio_get_block_data(to_buffer(b));
3443241b1d3SJoe Thornber }
3453241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_block_data);
3463241b1d3SJoe Thornber 
3473241b1d3SJoe Thornber struct buffer_aux {
3483241b1d3SJoe Thornber 	struct dm_block_validator *validator;
3493241b1d3SJoe Thornber 	int write_locked;
3502e8ed711SJoe Thornber 
3512e8ed711SJoe Thornber #ifdef CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING
3522e8ed711SJoe Thornber 	struct block_lock lock;
3532e8ed711SJoe Thornber #endif
3543241b1d3SJoe Thornber };
3553241b1d3SJoe Thornber 
dm_block_manager_alloc_callback(struct dm_buffer * buf)3563241b1d3SJoe Thornber static void dm_block_manager_alloc_callback(struct dm_buffer *buf)
3573241b1d3SJoe Thornber {
3583241b1d3SJoe Thornber 	struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
3590ef0b471SHeinz Mauelshagen 
3603241b1d3SJoe Thornber 	aux->validator = NULL;
3613241b1d3SJoe Thornber 	bl_init(&aux->lock);
3623241b1d3SJoe Thornber }
3633241b1d3SJoe Thornber 
dm_block_manager_write_callback(struct dm_buffer * buf)3643241b1d3SJoe Thornber static void dm_block_manager_write_callback(struct dm_buffer *buf)
3653241b1d3SJoe Thornber {
3663241b1d3SJoe Thornber 	struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
3670ef0b471SHeinz Mauelshagen 
3683241b1d3SJoe Thornber 	if (aux->validator) {
3693241b1d3SJoe Thornber 		aux->validator->prepare_for_write(aux->validator, (struct dm_block *) buf,
3703241b1d3SJoe Thornber 			 dm_bufio_get_block_size(dm_bufio_get_client(buf)));
3713241b1d3SJoe Thornber 	}
3723241b1d3SJoe Thornber }
3733241b1d3SJoe Thornber 
374a4a82ce3SHeinz Mauelshagen /*
375a4a82ce3SHeinz Mauelshagen  * -------------------------------------------------------------
3763241b1d3SJoe Thornber  * Public interface
377a4a82ce3SHeinz Mauelshagen  *--------------------------------------------------------------
378a4a82ce3SHeinz Mauelshagen  */
37951a0f659SJoe Thornber struct dm_block_manager {
38051a0f659SJoe Thornber 	struct dm_bufio_client *bufio;
38131097557SJoe Thornber 	bool read_only:1;
38251a0f659SJoe Thornber };
38351a0f659SJoe Thornber 
dm_block_manager_create(struct block_device * bdev,unsigned int block_size,unsigned int max_held_per_thread)3843241b1d3SJoe Thornber struct dm_block_manager *dm_block_manager_create(struct block_device *bdev,
38586a3238cSHeinz Mauelshagen 						 unsigned int block_size,
38686a3238cSHeinz Mauelshagen 						 unsigned int max_held_per_thread)
3873241b1d3SJoe Thornber {
38851a0f659SJoe Thornber 	int r;
38951a0f659SJoe Thornber 	struct dm_block_manager *bm;
39051a0f659SJoe Thornber 
39151a0f659SJoe Thornber 	bm = kmalloc(sizeof(*bm), GFP_KERNEL);
39251a0f659SJoe Thornber 	if (!bm) {
39351a0f659SJoe Thornber 		r = -ENOMEM;
39451a0f659SJoe Thornber 		goto bad;
39551a0f659SJoe Thornber 	}
39651a0f659SJoe Thornber 
39751a0f659SJoe Thornber 	bm->bufio = dm_bufio_client_create(bdev, block_size, max_held_per_thread,
3983241b1d3SJoe Thornber 					   sizeof(struct buffer_aux),
3993241b1d3SJoe Thornber 					   dm_block_manager_alloc_callback,
4000fcb100dSNathan Huckleberry 					   dm_block_manager_write_callback,
4010fcb100dSNathan Huckleberry 					   0);
40251a0f659SJoe Thornber 	if (IS_ERR(bm->bufio)) {
40351a0f659SJoe Thornber 		r = PTR_ERR(bm->bufio);
40451a0f659SJoe Thornber 		kfree(bm);
40551a0f659SJoe Thornber 		goto bad;
40651a0f659SJoe Thornber 	}
40751a0f659SJoe Thornber 
40831097557SJoe Thornber 	bm->read_only = false;
40931097557SJoe Thornber 
41051a0f659SJoe Thornber 	return bm;
41151a0f659SJoe Thornber 
41251a0f659SJoe Thornber bad:
41351a0f659SJoe Thornber 	return ERR_PTR(r);
4143241b1d3SJoe Thornber }
4153241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_block_manager_create);
4163241b1d3SJoe Thornber 
dm_block_manager_destroy(struct dm_block_manager * bm)4173241b1d3SJoe Thornber void dm_block_manager_destroy(struct dm_block_manager *bm)
4183241b1d3SJoe Thornber {
41951a0f659SJoe Thornber 	dm_bufio_client_destroy(bm->bufio);
42051a0f659SJoe Thornber 	kfree(bm);
4213241b1d3SJoe Thornber }
4223241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_block_manager_destroy);
4233241b1d3SJoe Thornber 
dm_block_manager_reset(struct dm_block_manager * bm)424*d4830012SLi Lingfeng void dm_block_manager_reset(struct dm_block_manager *bm)
425*d4830012SLi Lingfeng {
426*d4830012SLi Lingfeng 	dm_bufio_client_reset(bm->bufio);
427*d4830012SLi Lingfeng }
428*d4830012SLi Lingfeng EXPORT_SYMBOL_GPL(dm_block_manager_reset);
429*d4830012SLi Lingfeng 
dm_bm_block_size(struct dm_block_manager * bm)43086a3238cSHeinz Mauelshagen unsigned int dm_bm_block_size(struct dm_block_manager *bm)
4313241b1d3SJoe Thornber {
43251a0f659SJoe Thornber 	return dm_bufio_get_block_size(bm->bufio);
4333241b1d3SJoe Thornber }
4343241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_block_size);
4353241b1d3SJoe Thornber 
dm_bm_nr_blocks(struct dm_block_manager * bm)4363241b1d3SJoe Thornber dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm)
4373241b1d3SJoe Thornber {
43851a0f659SJoe Thornber 	return dm_bufio_get_device_size(bm->bufio);
4393241b1d3SJoe Thornber }
4403241b1d3SJoe Thornber 
dm_bm_validate_buffer(struct dm_block_manager * bm,struct dm_buffer * buf,struct buffer_aux * aux,struct dm_block_validator * v)4413241b1d3SJoe Thornber static int dm_bm_validate_buffer(struct dm_block_manager *bm,
4423241b1d3SJoe Thornber 				 struct dm_buffer *buf,
4433241b1d3SJoe Thornber 				 struct buffer_aux *aux,
4443241b1d3SJoe Thornber 				 struct dm_block_validator *v)
4453241b1d3SJoe Thornber {
4463241b1d3SJoe Thornber 	if (unlikely(!aux->validator)) {
4473241b1d3SJoe Thornber 		int r;
4480ef0b471SHeinz Mauelshagen 
4493241b1d3SJoe Thornber 		if (!v)
4503241b1d3SJoe Thornber 			return 0;
45151a0f659SJoe Thornber 		r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(bm->bufio));
452a5bd968aSMike Snitzer 		if (unlikely(r)) {
45389ddeb8cSMike Snitzer 			DMERR_LIMIT("%s validator check failed for block %llu", v->name,
454a5bd968aSMike Snitzer 				    (unsigned long long) dm_bufio_get_block_number(buf));
4553241b1d3SJoe Thornber 			return r;
456a5bd968aSMike Snitzer 		}
4573241b1d3SJoe Thornber 		aux->validator = v;
4583241b1d3SJoe Thornber 	} else {
4593241b1d3SJoe Thornber 		if (unlikely(aux->validator != v)) {
46089ddeb8cSMike Snitzer 			DMERR_LIMIT("validator mismatch (old=%s vs new=%s) for block %llu",
4613241b1d3SJoe Thornber 				    aux->validator->name, v ? v->name : "NULL",
46289ddeb8cSMike Snitzer 				    (unsigned long long) dm_bufio_get_block_number(buf));
4633241b1d3SJoe Thornber 			return -EINVAL;
4643241b1d3SJoe Thornber 		}
4653241b1d3SJoe Thornber 	}
4663241b1d3SJoe Thornber 
4673241b1d3SJoe Thornber 	return 0;
4683241b1d3SJoe Thornber }
dm_bm_read_lock(struct dm_block_manager * bm,dm_block_t b,struct dm_block_validator * v,struct dm_block ** result)4693241b1d3SJoe Thornber int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
4703241b1d3SJoe Thornber 		    struct dm_block_validator *v,
4713241b1d3SJoe Thornber 		    struct dm_block **result)
4723241b1d3SJoe Thornber {
4733241b1d3SJoe Thornber 	struct buffer_aux *aux;
4743241b1d3SJoe Thornber 	void *p;
4753241b1d3SJoe Thornber 	int r;
4763241b1d3SJoe Thornber 
47751a0f659SJoe Thornber 	p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
4785941c621SChengguang Xu 	if (IS_ERR(p))
4793241b1d3SJoe Thornber 		return PTR_ERR(p);
4803241b1d3SJoe Thornber 
4813241b1d3SJoe Thornber 	aux = dm_bufio_get_aux_data(to_buffer(*result));
4823241b1d3SJoe Thornber 	r = bl_down_read(&aux->lock);
4833241b1d3SJoe Thornber 	if (unlikely(r)) {
4843241b1d3SJoe Thornber 		dm_bufio_release(to_buffer(*result));
4853241b1d3SJoe Thornber 		report_recursive_bug(b, r);
4863241b1d3SJoe Thornber 		return r;
4873241b1d3SJoe Thornber 	}
4883241b1d3SJoe Thornber 
4893241b1d3SJoe Thornber 	aux->write_locked = 0;
4903241b1d3SJoe Thornber 
4913241b1d3SJoe Thornber 	r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
4923241b1d3SJoe Thornber 	if (unlikely(r)) {
4933241b1d3SJoe Thornber 		bl_up_read(&aux->lock);
4943241b1d3SJoe Thornber 		dm_bufio_release(to_buffer(*result));
4953241b1d3SJoe Thornber 		return r;
4963241b1d3SJoe Thornber 	}
4973241b1d3SJoe Thornber 
4983241b1d3SJoe Thornber 	return 0;
4993241b1d3SJoe Thornber }
5003241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_read_lock);
5013241b1d3SJoe Thornber 
dm_bm_write_lock(struct dm_block_manager * bm,dm_block_t b,struct dm_block_validator * v,struct dm_block ** result)5023241b1d3SJoe Thornber int dm_bm_write_lock(struct dm_block_manager *bm,
5033241b1d3SJoe Thornber 		     dm_block_t b, struct dm_block_validator *v,
5043241b1d3SJoe Thornber 		     struct dm_block **result)
5053241b1d3SJoe Thornber {
5063241b1d3SJoe Thornber 	struct buffer_aux *aux;
5073241b1d3SJoe Thornber 	void *p;
5083241b1d3SJoe Thornber 	int r;
5093241b1d3SJoe Thornber 
5103a653b20SYe Bin 	if (dm_bm_is_read_only(bm))
51131097557SJoe Thornber 		return -EPERM;
51231097557SJoe Thornber 
51351a0f659SJoe Thornber 	p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
5145941c621SChengguang Xu 	if (IS_ERR(p))
5153241b1d3SJoe Thornber 		return PTR_ERR(p);
5163241b1d3SJoe Thornber 
5173241b1d3SJoe Thornber 	aux = dm_bufio_get_aux_data(to_buffer(*result));
5183241b1d3SJoe Thornber 	r = bl_down_write(&aux->lock);
5193241b1d3SJoe Thornber 	if (r) {
5203241b1d3SJoe Thornber 		dm_bufio_release(to_buffer(*result));
5213241b1d3SJoe Thornber 		report_recursive_bug(b, r);
5223241b1d3SJoe Thornber 		return r;
5233241b1d3SJoe Thornber 	}
5243241b1d3SJoe Thornber 
5253241b1d3SJoe Thornber 	aux->write_locked = 1;
5263241b1d3SJoe Thornber 
5273241b1d3SJoe Thornber 	r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
5283241b1d3SJoe Thornber 	if (unlikely(r)) {
5293241b1d3SJoe Thornber 		bl_up_write(&aux->lock);
5303241b1d3SJoe Thornber 		dm_bufio_release(to_buffer(*result));
5313241b1d3SJoe Thornber 		return r;
5323241b1d3SJoe Thornber 	}
5333241b1d3SJoe Thornber 
5343241b1d3SJoe Thornber 	return 0;
5353241b1d3SJoe Thornber }
5363241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_write_lock);
5373241b1d3SJoe Thornber 
dm_bm_read_try_lock(struct dm_block_manager * bm,dm_block_t b,struct dm_block_validator * v,struct dm_block ** result)5383241b1d3SJoe Thornber int dm_bm_read_try_lock(struct dm_block_manager *bm,
5393241b1d3SJoe Thornber 			dm_block_t b, struct dm_block_validator *v,
5403241b1d3SJoe Thornber 			struct dm_block **result)
5413241b1d3SJoe Thornber {
5423241b1d3SJoe Thornber 	struct buffer_aux *aux;
5433241b1d3SJoe Thornber 	void *p;
5443241b1d3SJoe Thornber 	int r;
5453241b1d3SJoe Thornber 
54651a0f659SJoe Thornber 	p = dm_bufio_get(bm->bufio, b, (struct dm_buffer **) result);
5475941c621SChengguang Xu 	if (IS_ERR(p))
5483241b1d3SJoe Thornber 		return PTR_ERR(p);
5493241b1d3SJoe Thornber 	if (unlikely(!p))
5503241b1d3SJoe Thornber 		return -EWOULDBLOCK;
5513241b1d3SJoe Thornber 
5523241b1d3SJoe Thornber 	aux = dm_bufio_get_aux_data(to_buffer(*result));
5533241b1d3SJoe Thornber 	r = bl_down_read_nonblock(&aux->lock);
5543241b1d3SJoe Thornber 	if (r < 0) {
5553241b1d3SJoe Thornber 		dm_bufio_release(to_buffer(*result));
5563241b1d3SJoe Thornber 		report_recursive_bug(b, r);
5573241b1d3SJoe Thornber 		return r;
5583241b1d3SJoe Thornber 	}
5593241b1d3SJoe Thornber 	aux->write_locked = 0;
5603241b1d3SJoe Thornber 
5613241b1d3SJoe Thornber 	r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
5623241b1d3SJoe Thornber 	if (unlikely(r)) {
5633241b1d3SJoe Thornber 		bl_up_read(&aux->lock);
5643241b1d3SJoe Thornber 		dm_bufio_release(to_buffer(*result));
5653241b1d3SJoe Thornber 		return r;
5663241b1d3SJoe Thornber 	}
5673241b1d3SJoe Thornber 
5683241b1d3SJoe Thornber 	return 0;
5693241b1d3SJoe Thornber }
5703241b1d3SJoe Thornber 
dm_bm_write_lock_zero(struct dm_block_manager * bm,dm_block_t b,struct dm_block_validator * v,struct dm_block ** result)5713241b1d3SJoe Thornber int dm_bm_write_lock_zero(struct dm_block_manager *bm,
5723241b1d3SJoe Thornber 			  dm_block_t b, struct dm_block_validator *v,
5733241b1d3SJoe Thornber 			  struct dm_block **result)
5743241b1d3SJoe Thornber {
5753241b1d3SJoe Thornber 	int r;
5763241b1d3SJoe Thornber 	struct buffer_aux *aux;
5773241b1d3SJoe Thornber 	void *p;
5783241b1d3SJoe Thornber 
5793a653b20SYe Bin 	if (dm_bm_is_read_only(bm))
58031097557SJoe Thornber 		return -EPERM;
58131097557SJoe Thornber 
58251a0f659SJoe Thornber 	p = dm_bufio_new(bm->bufio, b, (struct dm_buffer **) result);
5835941c621SChengguang Xu 	if (IS_ERR(p))
5843241b1d3SJoe Thornber 		return PTR_ERR(p);
5853241b1d3SJoe Thornber 
5863241b1d3SJoe Thornber 	memset(p, 0, dm_bm_block_size(bm));
5873241b1d3SJoe Thornber 
5883241b1d3SJoe Thornber 	aux = dm_bufio_get_aux_data(to_buffer(*result));
5893241b1d3SJoe Thornber 	r = bl_down_write(&aux->lock);
5903241b1d3SJoe Thornber 	if (r) {
5913241b1d3SJoe Thornber 		dm_bufio_release(to_buffer(*result));
5923241b1d3SJoe Thornber 		return r;
5933241b1d3SJoe Thornber 	}
5943241b1d3SJoe Thornber 
5953241b1d3SJoe Thornber 	aux->write_locked = 1;
5963241b1d3SJoe Thornber 	aux->validator = v;
5973241b1d3SJoe Thornber 
5983241b1d3SJoe Thornber 	return 0;
5993241b1d3SJoe Thornber }
600384ef0e6SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_write_lock_zero);
6013241b1d3SJoe Thornber 
dm_bm_unlock(struct dm_block * b)6024c7da06fSMikulas Patocka void dm_bm_unlock(struct dm_block *b)
6033241b1d3SJoe Thornber {
6040ef0b471SHeinz Mauelshagen 	struct buffer_aux *aux = dm_bufio_get_aux_data(to_buffer(b));
6053241b1d3SJoe Thornber 
6063241b1d3SJoe Thornber 	if (aux->write_locked) {
6073241b1d3SJoe Thornber 		dm_bufio_mark_buffer_dirty(to_buffer(b));
6083241b1d3SJoe Thornber 		bl_up_write(&aux->lock);
6093241b1d3SJoe Thornber 	} else
6103241b1d3SJoe Thornber 		bl_up_read(&aux->lock);
6113241b1d3SJoe Thornber 
6123241b1d3SJoe Thornber 	dm_bufio_release(to_buffer(b));
6133241b1d3SJoe Thornber }
6143241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_unlock);
6153241b1d3SJoe Thornber 
dm_bm_flush(struct dm_block_manager * bm)616a9d45396SJoe Thornber int dm_bm_flush(struct dm_block_manager *bm)
6173241b1d3SJoe Thornber {
6183a653b20SYe Bin 	if (dm_bm_is_read_only(bm))
61931097557SJoe Thornber 		return -EPERM;
62031097557SJoe Thornber 
62151a0f659SJoe Thornber 	return dm_bufio_write_dirty_buffers(bm->bufio);
6223241b1d3SJoe Thornber }
623a9d45396SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_flush);
6243241b1d3SJoe Thornber 
dm_bm_prefetch(struct dm_block_manager * bm,dm_block_t b)62504f17c80SJoe Thornber void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b)
62604f17c80SJoe Thornber {
62704f17c80SJoe Thornber 	dm_bufio_prefetch(bm->bufio, b, 1);
62804f17c80SJoe Thornber }
62904f17c80SJoe Thornber 
dm_bm_is_read_only(struct dm_block_manager * bm)63049f154c7SMike Snitzer bool dm_bm_is_read_only(struct dm_block_manager *bm)
63149f154c7SMike Snitzer {
632beecc843SHeinz Mauelshagen 	return bm ? bm->read_only : true;
63349f154c7SMike Snitzer }
63449f154c7SMike Snitzer EXPORT_SYMBOL_GPL(dm_bm_is_read_only);
63549f154c7SMike Snitzer 
dm_bm_set_read_only(struct dm_block_manager * bm)63631097557SJoe Thornber void dm_bm_set_read_only(struct dm_block_manager *bm)
63731097557SJoe Thornber {
6383a653b20SYe Bin 	if (bm)
63931097557SJoe Thornber 		bm->read_only = true;
64031097557SJoe Thornber }
64131097557SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_set_read_only);
64231097557SJoe Thornber 
dm_bm_set_read_write(struct dm_block_manager * bm)6439b7aaa64SJoe Thornber void dm_bm_set_read_write(struct dm_block_manager *bm)
6449b7aaa64SJoe Thornber {
6453a653b20SYe Bin 	if (bm)
6469b7aaa64SJoe Thornber 		bm->read_only = false;
6479b7aaa64SJoe Thornber }
6489b7aaa64SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_set_read_write);
6499b7aaa64SJoe Thornber 
dm_bm_checksum(const void * data,size_t len,u32 init_xor)6503241b1d3SJoe Thornber u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor)
6513241b1d3SJoe Thornber {
6523241b1d3SJoe Thornber 	return crc32c(~(u32) 0, data, len) ^ init_xor;
6533241b1d3SJoe Thornber }
6543241b1d3SJoe Thornber EXPORT_SYMBOL_GPL(dm_bm_checksum);
6553241b1d3SJoe Thornber 
6563241b1d3SJoe Thornber /*----------------------------------------------------------------*/
6573241b1d3SJoe Thornber 
6583241b1d3SJoe Thornber MODULE_LICENSE("GPL");
6593241b1d3SJoe Thornber MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
6603241b1d3SJoe Thornber MODULE_DESCRIPTION("Immutable metadata library for dm");
6613241b1d3SJoe Thornber 
6623241b1d3SJoe Thornber /*----------------------------------------------------------------*/
663