summaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-bio-prison-v2.h
blob: 6e04234268db3217260f14908a27f0fd839199fd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 * Copyright (C) 2011-2017 Red Hat, Inc.
 *
 * This file is released under the GPL.
 */

#ifndef DM_BIO_PRISON_V2_H
#define DM_BIO_PRISON_V2_H

#include "persistent-data/dm-block-manager.h" /* FIXME: for dm_block_t */
#include "dm-thin-metadata.h" /* FIXME: for dm_thin_id */

#include <linux/bio.h>
#include <linux/rbtree.h>
#include <linux/workqueue.h>

/*----------------------------------------------------------------*/

int dm_bio_prison_init_v2(void);
void dm_bio_prison_exit_v2(void);

/*
 * Sometimes we can't deal with a bio straight away.  We put them in prison
 * where they can't cause any mischief.  Bios are put in a cell identified
 * by a key, multiple bios can be in the same cell.  When the cell is
 * subsequently unlocked the bios become available.
 */
struct dm_bio_prison_v2;

/*
 * Keys define a range of blocks within either a virtual or physical
 * device.
 */
struct dm_cell_key_v2 {
	int virtual;
	dm_thin_id dev;
	dm_block_t block_begin, block_end;
};

/*
 * Treat this as opaque, only in header so callers can manage allocation
 * themselves.
 */
struct dm_bio_prison_cell_v2 {
	// FIXME: pack these
	bool exclusive_lock;
	unsigned exclusive_level;
	unsigned shared_count;
	struct work_struct *quiesce_continuation;

	struct rb_node node;
	struct dm_cell_key_v2 key;
	struct bio_list bios;
};

struct dm_bio_prison_v2 *dm_bio_prison_create_v2(struct workqueue_struct *wq);
void dm_bio_prison_destroy_v2(struct dm_bio_prison_v2 *prison);

/*
 * These two functions just wrap a mempool.  This is a transitory step:
 * Eventually all bio prison clients should manage their own cell memory.
 *
 * Like mempool_alloc(), dm_bio_prison_alloc_cell_v2() can only fail if called
 * in interrupt context or passed GFP_NOWAIT.
 */
struct dm_bio_prison_cell_v2 *dm_bio_prison_alloc_cell_v2(struct dm_bio_prison_v2 *prison,
						    gfp_t gfp);
void dm_bio_prison_free_cell_v2(struct dm_bio_prison_v2 *prison,
				struct dm_bio_prison_cell_v2 *cell);

/*
 * Shared locks have a bio associated with them.
 *
 * If the lock is granted the caller can continue to use the bio, and must
 * call dm_cell_put_v2() to drop the reference count when finished using it.
 *
 * If the lock cannot be granted then the bio will be tracked within the
 * cell, and later given to the holder of the exclusive lock.
 *
 * See dm_cell_lock_v2() for discussion of the lock_level parameter.
 *
 * Compare *cell_result with cell_prealloc to see if the prealloc was used.
 * If cell_prealloc was used then inmate wasn't added to it.
 *
 * Returns true if the lock is granted.
 */
bool dm_cell_get_v2(struct dm_bio_prison_v2 *prison,
		    struct dm_cell_key_v2 *key,
		    unsigned lock_level,
		    struct bio *inmate,
		    struct dm_bio_prison_cell_v2 *cell_prealloc,
		    struct dm_bio_prison_cell_v2 **cell_result);

/*
 * Decrement the shared reference count for the lock.  Returns true if
 * returning ownership of the cell (ie. you should free it).
 */
bool dm_cell_put_v2(struct dm_bio_prison_v2 *prison,
		    struct dm_bio_prison_cell_v2 *cell);

/*
 * Locks a cell.  No associated bio.  Exclusive locks get priority.  These
 * locks constrain whether the io locks are granted according to level.
 *
 * Shared locks will still be granted if the lock_level is > (not = to) the
 * exclusive lock level.
 *
 * If an _exclusive_ lock is already held then -EBUSY is returned.
 *
 * Return values:
 *  < 0 - error
 *  0   - locked; no quiescing needed
 *  1   - locked; quiescing needed
 */
int dm_cell_lock_v2(struct dm_bio_prison_v2 *prison,
		    struct dm_cell_key_v2 *key,
		    unsigned lock_level,
		    struct dm_bio_prison_cell_v2 *cell_prealloc,
		    struct dm_bio_prison_cell_v2 **cell_result);

void dm_cell_quiesce_v2(struct dm_bio_prison_v2 *prison,
			struct dm_bio_prison_cell_v2 *cell,
			struct work_struct *continuation);

/*
 * Promotes an _exclusive_ lock to a higher lock level.
 *
 * Return values:
 *  < 0 - error
 *  0   - promoted; no quiescing needed
 *  1   - promoted; quiescing needed
 */
int dm_cell_lock_promote_v2(struct dm_bio_prison_v2 *prison,
			    struct dm_bio_prison_cell_v2 *cell,
			    unsigned new_lock_level);

/*
 * Adds any held bios to the bio list.
 *
 * There may be shared locks still held at this point even if you quiesced
 * (ie. different lock levels).
 *
 * Returns true if returning ownership of the cell (ie. you should free
 * it).
 */
bool dm_cell_unlock_v2(struct dm_bio_prison_v2 *prison,
		       struct dm_bio_prison_cell_v2 *cell,
		       struct bio_list *bios);

/*----------------------------------------------------------------*/

#endif