summaryrefslogtreecommitdiff
path: root/drivers/md/dm-vdo/vdo.h
blob: 483ae873e002ffc8e0ec6547263dd20db7eb396d (plain)
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright 2023 Red Hat
 */

#ifndef VDO_H
#define VDO_H

#include <linux/atomic.h>
#include <linux/blk_types.h>
#include <linux/completion.h>
#include <linux/dm-kcopyd.h>
#include <linux/list.h>
#include <linux/spinlock.h>

#include "admin-state.h"
#include "encodings.h"
#include "funnel-workqueue.h"
#include "packer.h"
#include "physical-zone.h"
#include "statistics.h"
#include "thread-registry.h"
#include "types.h"

enum notifier_state {
	/* Notifications are allowed but not in progress */
	MAY_NOTIFY,
	/* A notification is in progress */
	NOTIFYING,
	/* Notifications are not allowed */
	MAY_NOT_NOTIFY,
	/* A notification has completed */
	NOTIFIED,
};

/**
 * typedef vdo_read_only_notification_fn - A function to notify a listener that the VDO has gone
 *                                         read-only.
 * @listener: The object to notify.
 * @parent: The completion to notify in order to acknowledge the notification.
 */
typedef void (*vdo_read_only_notification_fn)(void *listener, struct vdo_completion *parent);

/*
 * An object to be notified when the VDO enters read-only mode
 */
struct read_only_listener {
	/* The listener */
	void *listener;
	/* The method to call to notify the listener */
	vdo_read_only_notification_fn notify;
	/* A pointer to the next listener */
	struct read_only_listener *next;
};

struct vdo_thread {
	struct vdo *vdo;
	thread_id_t thread_id;
	struct vdo_work_queue *queue;
	/*
	 * Each thread maintains its own notion of whether the VDO is read-only so that the
	 * read-only state can be checked from any base thread without worrying about
	 * synchronization or thread safety. This does mean that knowledge of the VDO going
	 * read-only does not occur simultaneously across the VDO's threads, but that does not seem
	 * to cause any problems.
	 */
	bool is_read_only;
	/*
	 * A list of objects waiting to be notified on this thread that the VDO has entered
	 * read-only mode.
	 */
	struct read_only_listener *listeners;
	struct registered_thread allocating_thread;
};

/* Keep struct bio statistics atomically */
struct atomic_bio_stats {
	atomic64_t read; /* Number of not REQ_WRITE bios */
	atomic64_t write; /* Number of REQ_WRITE bios */
	atomic64_t discard; /* Number of REQ_DISCARD bios */
	atomic64_t flush; /* Number of REQ_FLUSH bios */
	atomic64_t empty_flush; /* Number of REQ_PREFLUSH bios without data */
	atomic64_t fua; /* Number of REQ_FUA bios */
};

/* Counters are atomic since updates can arrive concurrently from arbitrary threads. */
struct atomic_statistics {
	atomic64_t bios_submitted;
	atomic64_t bios_completed;
	atomic64_t flush_out;
	atomic64_t invalid_advice_pbn_count;
	atomic64_t no_space_error_count;
	atomic64_t read_only_error_count;
	struct atomic_bio_stats bios_in;
	struct atomic_bio_stats bios_in_partial;
	struct atomic_bio_stats bios_out;
	struct atomic_bio_stats bios_out_completed;
	struct atomic_bio_stats bios_acknowledged;
	struct atomic_bio_stats bios_acknowledged_partial;
	struct atomic_bio_stats bios_meta;
	struct atomic_bio_stats bios_meta_completed;
	struct atomic_bio_stats bios_journal;
	struct atomic_bio_stats bios_journal_completed;
	struct atomic_bio_stats bios_page_cache;
	struct atomic_bio_stats bios_page_cache_completed;
};

struct read_only_notifier {
	/* The completion for entering read-only mode */
	struct vdo_completion completion;
	/* A completion waiting for notifications to be drained or enabled */
	struct vdo_completion *waiter;
	/* Lock to protect the next two fields */
	spinlock_t lock;
	/* The code of the error which put the VDO into read-only mode */
	int read_only_error;
	/* The current state of the notifier (values described above) */
	enum notifier_state state;
};

/*
 * The thread ID returned when the current thread is not a vdo thread, or can not be determined
 * (usually due to being at interrupt context).
 */
#define VDO_INVALID_THREAD_ID ((thread_id_t) -1)

struct thread_config {
	zone_count_t logical_zone_count;
	zone_count_t physical_zone_count;
	zone_count_t hash_zone_count;
	thread_count_t bio_thread_count;
	thread_count_t thread_count;
	thread_id_t admin_thread;
	thread_id_t journal_thread;
	thread_id_t packer_thread;
	thread_id_t dedupe_thread;
	thread_id_t bio_ack_thread;
	thread_id_t cpu_thread;
	thread_id_t *logical_threads;
	thread_id_t *physical_threads;
	thread_id_t *hash_zone_threads;
	thread_id_t *bio_threads;
};

struct thread_count_config;

struct vdo_super_block {
	/* The vio for reading and writing the super block to disk */
	struct vio vio;
	/* A buffer to hold the super block */
	u8 *buffer;
	/* Whether this super block may not be written */
	bool unwritable;
};

struct data_vio_pool;

struct vdo_administrator {
	struct vdo_completion completion;
	struct admin_state state;
	atomic_t busy;
	u32 phase;
	struct completion callback_sync;
};

struct vdo {
	char thread_name_prefix[MAX_VDO_WORK_QUEUE_NAME_LEN];
	struct vdo_thread *threads;
	vdo_action_fn action;
	struct vdo_completion *completion;
	struct vio_tracer *vio_tracer;

	/* The atomic version of the state of this vdo */
	atomic_t state;
	/* The full state of all components */
	struct vdo_component_states states;
	/*
	 * A counter value to attach to thread names and log messages to identify the individual
	 * device.
	 */
	unsigned int instance;
	/* The read-only notifier */
	struct read_only_notifier read_only_notifier;
	/* The load-time configuration of this vdo */
	struct device_config *device_config;
	/* The thread mapping */
	struct thread_config thread_config;

	/* The super block */
	struct vdo_super_block super_block;

	/* The partitioning of the underlying storage */
	struct layout layout;
	struct layout next_layout;
	struct dm_kcopyd_client *partition_copier;

	/* The block map */
	struct block_map *block_map;

	/* The journal for block map recovery */
	struct recovery_journal *recovery_journal;

	/* The slab depot */
	struct slab_depot *depot;

	/* The compressed-block packer */
	struct packer *packer;
	/* Whether incoming data should be compressed */
	bool compressing;

	/* The handler for flush requests */
	struct flusher *flusher;

	/* The state the vdo was in when loaded (primarily for unit tests) */
	enum vdo_state load_state;

	/* The logical zones of this vdo */
	struct logical_zones *logical_zones;

	/* The physical zones of this vdo */
	struct physical_zones *physical_zones;

	/* The hash lock zones of this vdo */
	struct hash_zones *hash_zones;

	/* Bio submission manager used for sending bios to the storage device. */
	struct io_submitter *io_submitter;

	/* The pool of data_vios for servicing incoming bios */
	struct data_vio_pool *data_vio_pool;

	/* The manager for administrative operations */
	struct vdo_administrator admin;

	/* Flags controlling administrative operations */
	const struct admin_state_code *suspend_type;
	bool allocations_allowed;
	bool dump_on_shutdown;
	atomic_t processing_message;

	/*
	 * Statistics
	 * Atomic stats counters
	 */
	struct atomic_statistics stats;
	/* Used to gather statistics without allocating memory */
	struct vdo_statistics stats_buffer;
	/* Protects the stats_buffer */
	struct mutex stats_mutex;

	/* A list of all device_configs referencing this vdo */
	struct list_head device_config_list;

	/* This VDO's list entry for the device registry */
	struct list_head registration;

	/* Underlying block device info. */
	u64 starting_sector_offset;
	struct volume_geometry geometry;

	/* N blobs of context data for LZ4 code, one per CPU thread. */
	char **compression_context;
};

/**
 * vdo_uses_bio_ack_queue() - Indicate whether the vdo is configured to use a separate work queue
 *                            for acknowledging received and processed bios.
 * @vdo: The vdo.
 *
 * Note that this directly controls the handling of write operations, but the compile-time flag
 * VDO_USE_BIO_ACK_QUEUE_FOR_READ is also checked for read operations.
 *
 * Return: Whether a bio-acknowledgement work queue is in use.
 */
static inline bool vdo_uses_bio_ack_queue(struct vdo *vdo)
{
	return vdo->device_config->thread_counts.bio_ack_threads > 0;
}

/**
 * typedef vdo_filter_fn - Method type for vdo matching methods.
 *
 * A filter function returns false if the vdo doesn't match.
 */
typedef bool (*vdo_filter_fn)(struct vdo *vdo, const void *context);

void vdo_initialize_device_registry_once(void);
struct vdo * __must_check vdo_find_matching(vdo_filter_fn filter, const void *context);

int __must_check vdo_make_thread(struct vdo *vdo, thread_id_t thread_id,
				 const struct vdo_work_queue_type *type,
				 unsigned int queue_count, void *contexts[]);

static inline int __must_check vdo_make_default_thread(struct vdo *vdo,
						       thread_id_t thread_id)
{
	return vdo_make_thread(vdo, thread_id, NULL, 1, NULL);
}

int __must_check vdo_make(unsigned int instance, struct device_config *config,
			  char **reason, struct vdo **vdo_ptr);

void vdo_destroy(struct vdo *vdo);

void vdo_load_super_block(struct vdo *vdo, struct vdo_completion *parent);

struct block_device * __must_check vdo_get_backing_device(const struct vdo *vdo);

const char * __must_check vdo_get_device_name(const struct dm_target *target);

int __must_check vdo_synchronous_flush(struct vdo *vdo);

const struct admin_state_code * __must_check vdo_get_admin_state(const struct vdo *vdo);

bool vdo_set_compressing(struct vdo *vdo, bool enable);

bool vdo_get_compressing(struct vdo *vdo);

void vdo_fetch_statistics(struct vdo *vdo, struct vdo_statistics *stats);

thread_id_t vdo_get_callback_thread_id(void);

enum vdo_state __must_check vdo_get_state(const struct vdo *vdo);

void vdo_set_state(struct vdo *vdo, enum vdo_state state);

void vdo_save_components(struct vdo *vdo, struct vdo_completion *parent);

int vdo_register_read_only_listener(struct vdo *vdo, void *listener,
				    vdo_read_only_notification_fn notification,
				    thread_id_t thread_id);

int vdo_enable_read_only_entry(struct vdo *vdo);

void vdo_wait_until_not_entering_read_only_mode(struct vdo_completion *parent);

void vdo_allow_read_only_mode_entry(struct vdo_completion *parent);

void vdo_enter_read_only_mode(struct vdo *vdo, int error_code);

bool __must_check vdo_is_read_only(struct vdo *vdo);

bool __must_check vdo_in_read_only_mode(const struct vdo *vdo);

bool __must_check vdo_in_recovery_mode(const struct vdo *vdo);

void vdo_enter_recovery_mode(struct vdo *vdo);

void vdo_assert_on_admin_thread(const struct vdo *vdo, const char *name);

void vdo_assert_on_logical_zone_thread(const struct vdo *vdo, zone_count_t logical_zone,
				       const char *name);

void vdo_assert_on_physical_zone_thread(const struct vdo *vdo, zone_count_t physical_zone,
					const char *name);

int __must_check vdo_get_physical_zone(const struct vdo *vdo, physical_block_number_t pbn,
				       struct physical_zone **zone_ptr);

void vdo_dump_status(const struct vdo *vdo);

#endif /* VDO_H */