summaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-vdo/encodings.h
blob: e5ff2b0aaa79718c231447d80853556841d52946 (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
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright 2023 Red Hat
 */

#ifndef VDO_ENCODINGS_H
#define VDO_ENCODINGS_H

#include <linux/blk_types.h>
#include <linux/crc32.h>
#include <linux/limits.h>
#include <linux/uuid.h>

#include "numeric.h"

#include "constants.h"
#include "types.h"

/*
 * An in-memory representation of a version number for versioned structures on disk.
 *
 * A version number consists of two portions, a major version and a minor version. Any format
 * change which does not require an explicit upgrade step from the previous version should
 * increment the minor version. Any format change which either requires an explicit upgrade step,
 * or is wholly incompatible (i.e. can not be upgraded to), should increment the major version, and
 * set the minor version to 0.
 */
struct version_number {
	u32 major_version;
	u32 minor_version;
};

/*
 * A packed, machine-independent, on-disk representation of a version_number. Both fields are
 * stored in little-endian byte order.
 */
struct packed_version_number {
	__le32 major_version;
	__le32 minor_version;
} __packed;

/* The registry of component ids for use in headers */
#define VDO_SUPER_BLOCK 0
#define VDO_LAYOUT 1
#define VDO_RECOVERY_JOURNAL 2
#define VDO_SLAB_DEPOT 3
#define VDO_BLOCK_MAP 4
#define VDO_GEOMETRY_BLOCK 5

/* The header for versioned data stored on disk. */
struct header {
	u32 id; /* The component this is a header for */
	struct version_number version; /* The version of the data format */
	size_t size; /* The size of the data following this header */
};

/* A packed, machine-independent, on-disk representation of a component header. */
struct packed_header {
	__le32 id;
	struct packed_version_number version;
	__le64 size;
} __packed;

enum {
	VDO_GEOMETRY_BLOCK_LOCATION = 0,
	VDO_GEOMETRY_MAGIC_NUMBER_SIZE = 8,
	VDO_DEFAULT_GEOMETRY_BLOCK_VERSION = 5,
};

struct index_config {
	u32 mem;
	u32 unused;
	bool sparse;
} __packed;

enum volume_region_id {
	VDO_INDEX_REGION = 0,
	VDO_DATA_REGION = 1,
	VDO_VOLUME_REGION_COUNT,
};

struct volume_region {
	/* The ID of the region */
	enum volume_region_id id;
	/*
	 * The absolute starting offset on the device. The region continues until the next region
	 * begins.
	 */
	physical_block_number_t start_block;
} __packed;

struct volume_geometry {
	/* For backwards compatibility */
	u32 unused;
	/* The nonce of this volume */
	nonce_t nonce;
	/* The uuid of this volume */
	uuid_t uuid;
	/* The block offset to be applied to bios */
	block_count_t bio_offset;
	/* The regions in ID order */
	struct volume_region regions[VDO_VOLUME_REGION_COUNT];
	/* The index config */
	struct index_config index_config;
} __packed;

/* This volume geometry struct is used for sizing only */
struct volume_geometry_4_0 {
	/* For backwards compatibility */
	u32 unused;
	/* The nonce of this volume */
	nonce_t nonce;
	/* The uuid of this volume */
	uuid_t uuid;
	/* The regions in ID order */
	struct volume_region regions[VDO_VOLUME_REGION_COUNT];
	/* The index config */
	struct index_config index_config;
} __packed;

extern const u8 VDO_GEOMETRY_MAGIC_NUMBER[VDO_GEOMETRY_MAGIC_NUMBER_SIZE + 1];

/**
 * DOC: Block map entries
 *
 * The entry for each logical block in the block map is encoded into five bytes, which saves space
 * in both the on-disk and in-memory layouts. It consists of the 36 low-order bits of a
 * physical_block_number_t (addressing 256 terabytes with a 4KB block size) and a 4-bit encoding of
 * a block_mapping_state.
 *
 * Of the 8 high bits of the 5-byte structure:
 *
 * Bits 7..4: The four highest bits of the 36-bit physical block number
 * Bits 3..0: The 4-bit block_mapping_state
 *
 * The following 4 bytes are the low order bytes of the physical block number, in little-endian
 * order.
 *
 * Conversion functions to and from a data location are provided.
 */
struct block_map_entry {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	unsigned mapping_state : 4;
	unsigned pbn_high_nibble : 4;
#else
	unsigned pbn_high_nibble : 4;
	unsigned mapping_state : 4;
#endif

	__le32 pbn_low_word;
} __packed;

struct block_map_page_header {
	__le64 nonce;
	__le64 pbn;

	/* May be non-zero on disk */
	u8 unused_long_word[8];

	/* Whether this page has been written twice to disk */
	bool initialized;

	/* Always zero on disk */
	u8 unused_byte1;

	/* May be non-zero on disk */
	u8 unused_byte2;
	u8 unused_byte3;
} __packed;

struct block_map_page {
	struct packed_version_number version;
	struct block_map_page_header header;
	struct block_map_entry entries[];
} __packed;

enum block_map_page_validity {
	VDO_BLOCK_MAP_PAGE_VALID,
	VDO_BLOCK_MAP_PAGE_INVALID,
	/* Valid page found in the wrong location on disk */
	VDO_BLOCK_MAP_PAGE_BAD,
};

struct block_map_state_2_0 {
	physical_block_number_t flat_page_origin;
	block_count_t flat_page_count;
	physical_block_number_t root_origin;
	block_count_t root_count;
} __packed;

struct boundary {
	page_number_t levels[VDO_BLOCK_MAP_TREE_HEIGHT];
};

extern const struct header VDO_BLOCK_MAP_HEADER_2_0;

/* The state of the recovery journal as encoded in the VDO super block. */
struct recovery_journal_state_7_0 {
	/* Sequence number to start the journal */
	sequence_number_t journal_start;
	/* Number of logical blocks used by VDO */
	block_count_t logical_blocks_used;
	/* Number of block map pages allocated */
	block_count_t block_map_data_blocks;
} __packed;

extern const struct header VDO_RECOVERY_JOURNAL_HEADER_7_0;

typedef u16 journal_entry_count_t;

/*
 * A recovery journal entry stores three physical locations: a data location that is the value of a
 * single mapping in the block map tree, and the two locations of the block map pages and slots
 * that are acquiring and releasing a reference to the location. The journal entry also stores an
 * operation code that says whether the mapping is for a logical block or for the block map tree
 * itself.
 */
struct recovery_journal_entry {
	struct block_map_slot slot;
	struct data_location mapping;
	struct data_location unmapping;
	enum journal_operation operation;
};

/* The packed, on-disk representation of a recovery journal entry. */
struct packed_recovery_journal_entry {
	/*
	 * In little-endian bit order:
	 * Bits 15..12: The four highest bits of the 36-bit physical block number of the block map
	 * tree page
	 * Bits 11..2: The 10-bit block map page slot number
	 * Bit 1..0: The journal_operation of the entry (this actually only requires 1 bit, but
	 *           it is convenient to keep the extra bit as part of this field.
	 */
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	unsigned operation : 2;
	unsigned slot_low : 6;
	unsigned slot_high : 4;
	unsigned pbn_high_nibble : 4;
#else
	unsigned slot_low : 6;
	unsigned operation : 2;
	unsigned pbn_high_nibble : 4;
	unsigned slot_high : 4;
#endif

	/*
	 * Bits 47..16: The 32 low-order bits of the block map page PBN, in little-endian byte
	 * order
	 */
	__le32 pbn_low_word;

	/*
	 * Bits 87..48: The five-byte block map entry encoding the location that will be stored in
	 * the block map page slot
	 */
	struct block_map_entry mapping;

	/*
	 * Bits 127..88: The five-byte block map entry encoding the location that was stored in the
	 * block map page slot
	 */
	struct block_map_entry unmapping;
} __packed;

/* The packed, on-disk representation of an old format recovery journal entry. */
struct packed_recovery_journal_entry_1 {
	/*
	 * In little-endian bit order:
	 * Bits 15..12: The four highest bits of the 36-bit physical block number of the block map
	 *              tree page
	 * Bits 11..2: The 10-bit block map page slot number
	 * Bits 1..0: The 2-bit journal_operation of the entry
	 *
	 */
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	unsigned operation : 2;
	unsigned slot_low : 6;
	unsigned slot_high : 4;
	unsigned pbn_high_nibble : 4;
#else
	unsigned slot_low : 6;
	unsigned operation : 2;
	unsigned pbn_high_nibble : 4;
	unsigned slot_high : 4;
#endif

	/*
	 * Bits 47..16: The 32 low-order bits of the block map page PBN, in little-endian byte
	 * order
	 */
	__le32 pbn_low_word;

	/*
	 * Bits 87..48: The five-byte block map entry encoding the location that was or will be
	 * stored in the block map page slot
	 */
	struct block_map_entry block_map_entry;
} __packed;

enum journal_operation_1 {
	VDO_JOURNAL_DATA_DECREMENT = 0,
	VDO_JOURNAL_DATA_INCREMENT = 1,
	VDO_JOURNAL_BLOCK_MAP_DECREMENT = 2,
	VDO_JOURNAL_BLOCK_MAP_INCREMENT = 3,
} __packed;

struct recovery_block_header {
	sequence_number_t block_map_head; /* Block map head sequence number */
	sequence_number_t slab_journal_head; /* Slab journal head seq. number */
	sequence_number_t sequence_number; /* Sequence number for this block */
	nonce_t nonce; /* A given VDO instance's nonce */
	block_count_t logical_blocks_used; /* Logical blocks in use */
	block_count_t block_map_data_blocks; /* Allocated block map pages */
	journal_entry_count_t entry_count; /* Number of entries written */
	u8 check_byte; /* The protection check byte */
	u8 recovery_count; /* Number of recoveries completed */
	enum vdo_metadata_type metadata_type; /* Metadata type */
};

/*
 * The packed, on-disk representation of a recovery journal block header. All fields are kept in
 * little-endian byte order.
 */
struct packed_journal_header {
	/* Block map head 64-bit sequence number */
	__le64 block_map_head;

	/* Slab journal head 64-bit sequence number */
	__le64 slab_journal_head;

	/* The 64-bit sequence number for this block */
	__le64 sequence_number;

	/* A given VDO instance's 64-bit nonce */
	__le64 nonce;

	/* 8-bit metadata type (should always be one for the recovery journal) */
	u8 metadata_type;

	/* 16-bit count of the entries encoded in the block */
	__le16 entry_count;

	/* 64-bit count of the logical blocks used when this block was opened */
	__le64 logical_blocks_used;

	/* 64-bit count of the block map blocks used when this block was opened */
	__le64 block_map_data_blocks;

	/* The protection check byte */
	u8 check_byte;

	/* The number of recoveries completed */
	u8 recovery_count;
} __packed;

struct packed_journal_sector {
	/* The protection check byte */
	u8 check_byte;

	/* The number of recoveries completed */
	u8 recovery_count;

	/* The number of entries in this sector */
	u8 entry_count;

	/* Journal entries for this sector */
	struct packed_recovery_journal_entry entries[];
} __packed;

enum {
	/* The number of entries in each sector (except the last) when filled */
	RECOVERY_JOURNAL_ENTRIES_PER_SECTOR =
		((VDO_SECTOR_SIZE - sizeof(struct packed_journal_sector)) /
		 sizeof(struct packed_recovery_journal_entry)),
	RECOVERY_JOURNAL_ENTRIES_PER_BLOCK = RECOVERY_JOURNAL_ENTRIES_PER_SECTOR * 7,
	/* The number of entries in a v1 recovery journal block. */
	RECOVERY_JOURNAL_1_ENTRIES_PER_BLOCK = 311,
	/* The number of entries in each v1 sector (except the last) when filled */
	RECOVERY_JOURNAL_1_ENTRIES_PER_SECTOR =
		((VDO_SECTOR_SIZE - sizeof(struct packed_journal_sector)) /
		 sizeof(struct packed_recovery_journal_entry_1)),
	/* The number of entries in the last sector when a block is full */
	RECOVERY_JOURNAL_1_ENTRIES_IN_LAST_SECTOR =
		(RECOVERY_JOURNAL_1_ENTRIES_PER_BLOCK % RECOVERY_JOURNAL_1_ENTRIES_PER_SECTOR),
};

/* A type representing a reference count of a block. */
typedef u8 vdo_refcount_t;

/* The absolute position of an entry in a recovery journal or slab journal. */
struct journal_point {
	sequence_number_t sequence_number;
	journal_entry_count_t entry_count;
};

/* A packed, platform-independent encoding of a struct journal_point. */
struct packed_journal_point {
	/*
	 * The packed representation is the little-endian 64-bit representation of the low-order 48
	 * bits of the sequence number, shifted up 16 bits, or'ed with the 16-bit entry count.
	 *
	 * Very long-term, the top 16 bits of the sequence number may not always be zero, as this
	 * encoding assumes--see BZ 1523240.
	 */
	__le64 encoded_point;
} __packed;

/* Special vdo_refcount_t values. */
#define EMPTY_REFERENCE_COUNT 0
enum {
	MAXIMUM_REFERENCE_COUNT = 254,
	PROVISIONAL_REFERENCE_COUNT = 255,
};

enum {
	COUNTS_PER_SECTOR =
		((VDO_SECTOR_SIZE - sizeof(struct packed_journal_point)) / sizeof(vdo_refcount_t)),
	COUNTS_PER_BLOCK = COUNTS_PER_SECTOR * VDO_SECTORS_PER_BLOCK,
};

/* The format of each sector of a reference_block on disk. */
struct packed_reference_sector {
	struct packed_journal_point commit_point;
	vdo_refcount_t counts[COUNTS_PER_SECTOR];
} __packed;

struct packed_reference_block {
	struct packed_reference_sector sectors[VDO_SECTORS_PER_BLOCK];
};

struct slab_depot_state_2_0 {
	struct slab_config slab_config;
	physical_block_number_t first_block;
	physical_block_number_t last_block;
	zone_count_t zone_count;
} __packed;

extern const struct header VDO_SLAB_DEPOT_HEADER_2_0;

/*
 * vdo_slab journal blocks may have one of two formats, depending upon whether or not any of the
 * entries in the block are block map increments. Since the steady state for a VDO is that all of
 * the necessary block map pages will be allocated, most slab journal blocks will have only data
 * entries. Such blocks can hold more entries, hence the two formats.
 */

/* A single slab journal entry */
struct slab_journal_entry {
	slab_block_number sbn;
	enum journal_operation operation;
	bool increment;
};

/* A single slab journal entry in its on-disk form */
typedef struct {
	u8 offset_low8;
	u8 offset_mid8;

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	unsigned offset_high7 : 7;
	unsigned increment : 1;
#else
	unsigned increment : 1;
	unsigned offset_high7 : 7;
#endif
} __packed packed_slab_journal_entry;

/* The unpacked representation of the header of a slab journal block */
struct slab_journal_block_header {
	/* Sequence number for head of journal */
	sequence_number_t head;
	/* Sequence number for this block */
	sequence_number_t sequence_number;
	/* The nonce for a given VDO instance */
	nonce_t nonce;
	/* Recovery journal point for last entry */
	struct journal_point recovery_point;
	/* Metadata type */
	enum vdo_metadata_type metadata_type;
	/* Whether this block contains block map increments */
	bool has_block_map_increments;
	/* The number of entries in the block */
	journal_entry_count_t entry_count;
};

/*
 * The packed, on-disk representation of a slab journal block header. All fields are kept in
 * little-endian byte order.
 */
struct packed_slab_journal_block_header {
	/* 64-bit sequence number for head of journal */
	__le64 head;
	/* 64-bit sequence number for this block */
	__le64 sequence_number;
	/* Recovery journal point for the last entry, packed into 64 bits */
	struct packed_journal_point recovery_point;
	/* The 64-bit nonce for a given VDO instance */
	__le64 nonce;
	/* 8-bit metadata type (should always be two, for the slab journal) */
	u8 metadata_type;
	/* Whether this block contains block map increments */
	bool has_block_map_increments;
	/* 16-bit count of the entries encoded in the block */
	__le16 entry_count;
} __packed;

enum {
	VDO_SLAB_JOURNAL_PAYLOAD_SIZE =
		VDO_BLOCK_SIZE - sizeof(struct packed_slab_journal_block_header),
	VDO_SLAB_JOURNAL_FULL_ENTRIES_PER_BLOCK = (VDO_SLAB_JOURNAL_PAYLOAD_SIZE * 8) / 25,
	VDO_SLAB_JOURNAL_ENTRY_TYPES_SIZE =
		((VDO_SLAB_JOURNAL_FULL_ENTRIES_PER_BLOCK - 1) / 8) + 1,
	VDO_SLAB_JOURNAL_ENTRIES_PER_BLOCK =
		(VDO_SLAB_JOURNAL_PAYLOAD_SIZE / sizeof(packed_slab_journal_entry)),
};

/* The payload of a slab journal block which has block map increments */
struct full_slab_journal_entries {
	/* The entries themselves */
	packed_slab_journal_entry entries[VDO_SLAB_JOURNAL_FULL_ENTRIES_PER_BLOCK];
	/* The bit map indicating which entries are block map increments */
	u8 entry_types[VDO_SLAB_JOURNAL_ENTRY_TYPES_SIZE];
} __packed;

typedef union {
	/* Entries which include block map increments */
	struct full_slab_journal_entries full_entries;
	/* Entries which are only data updates */
	packed_slab_journal_entry entries[VDO_SLAB_JOURNAL_ENTRIES_PER_BLOCK];
	/* Ensure the payload fills to the end of the block */
	u8 space[VDO_SLAB_JOURNAL_PAYLOAD_SIZE];
} __packed slab_journal_payload;

struct packed_slab_journal_block {
	struct packed_slab_journal_block_header header;
	slab_journal_payload payload;
} __packed;

/* The offset of a slab journal tail block. */
typedef u8 tail_block_offset_t;

struct slab_summary_entry {
	/* Bits 7..0: The offset of the tail block within the slab journal */
	tail_block_offset_t tail_block_offset;

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	/* Bits 13..8: A hint about the fullness of the slab */
	unsigned int fullness_hint : 6;
	/* Bit 14: Whether the ref_counts must be loaded from the layer */
	unsigned int load_ref_counts : 1;
	/* Bit 15: The believed cleanliness of this slab */
	unsigned int is_dirty : 1;
#else
	/* Bit 15: The believed cleanliness of this slab */
	unsigned int is_dirty : 1;
	/* Bit 14: Whether the ref_counts must be loaded from the layer */
	unsigned int load_ref_counts : 1;
	/* Bits 13..8: A hint about the fullness of the slab */
	unsigned int fullness_hint : 6;
#endif
} __packed;

enum {
	VDO_SLAB_SUMMARY_FULLNESS_HINT_BITS = 6,
	VDO_SLAB_SUMMARY_ENTRIES_PER_BLOCK = VDO_BLOCK_SIZE / sizeof(struct slab_summary_entry),
	VDO_SLAB_SUMMARY_BLOCKS_PER_ZONE = MAX_VDO_SLABS / VDO_SLAB_SUMMARY_ENTRIES_PER_BLOCK,
	VDO_SLAB_SUMMARY_BLOCKS = VDO_SLAB_SUMMARY_BLOCKS_PER_ZONE * MAX_VDO_PHYSICAL_ZONES,
};

struct layout {
	physical_block_number_t start;
	block_count_t size;
	physical_block_number_t first_free;
	physical_block_number_t last_free;
	size_t num_partitions;
	struct partition *head;
};

struct partition {
	enum partition_id id; /* The id of this partition */
	physical_block_number_t offset; /* The offset into the layout of this partition */
	block_count_t count; /* The number of blocks in the partition */
	struct partition *next; /* A pointer to the next partition in the layout */
};

struct layout_3_0 {
	physical_block_number_t first_free;
	physical_block_number_t last_free;
	u8 partition_count;
} __packed;

struct partition_3_0 {
	enum partition_id id;
	physical_block_number_t offset;
	physical_block_number_t base; /* unused but retained for backwards compatibility */
	block_count_t count;
} __packed;

/*
 * The configuration of the VDO service.
 */
struct vdo_config {
	block_count_t logical_blocks; /* number of logical blocks */
	block_count_t physical_blocks; /* number of physical blocks */
	block_count_t slab_size; /* number of blocks in a slab */
	block_count_t recovery_journal_size; /* number of recovery journal blocks */
	block_count_t slab_journal_blocks; /* number of slab journal blocks */
};

/* This is the structure that captures the vdo fields saved as a super block component. */
struct vdo_component {
	enum vdo_state state;
	u64 complete_recoveries;
	u64 read_only_recoveries;
	struct vdo_config config;
	nonce_t nonce;
};

/*
 * A packed, machine-independent, on-disk representation of the vdo_config in the VDO component
 * data in the super block.
 */
struct packed_vdo_config {
	__le64 logical_blocks;
	__le64 physical_blocks;
	__le64 slab_size;
	__le64 recovery_journal_size;
	__le64 slab_journal_blocks;
} __packed;

/*
 * A packed, machine-independent, on-disk representation of version 41.0 of the VDO component data
 * in the super block.
 */
struct packed_vdo_component_41_0 {
	__le32 state;
	__le64 complete_recoveries;
	__le64 read_only_recoveries;
	struct packed_vdo_config config;
	__le64 nonce;
} __packed;

/*
 * The version of the on-disk format of a VDO volume. This should be incremented any time the
 * on-disk representation of any VDO structure changes. Changes which require only online upgrade
 * steps should increment the minor version. Changes which require an offline upgrade or which can
 * not be upgraded to at all should increment the major version and set the minor version to 0.
 */
extern const struct version_number VDO_VOLUME_VERSION_67_0;

enum {
	VDO_ENCODED_HEADER_SIZE = sizeof(struct packed_header),
	BLOCK_MAP_COMPONENT_ENCODED_SIZE =
		VDO_ENCODED_HEADER_SIZE + sizeof(struct block_map_state_2_0),
	RECOVERY_JOURNAL_COMPONENT_ENCODED_SIZE =
		VDO_ENCODED_HEADER_SIZE + sizeof(struct recovery_journal_state_7_0),
	SLAB_DEPOT_COMPONENT_ENCODED_SIZE =
		VDO_ENCODED_HEADER_SIZE + sizeof(struct slab_depot_state_2_0),
	VDO_PARTITION_COUNT = 4,
	VDO_LAYOUT_ENCODED_SIZE = (VDO_ENCODED_HEADER_SIZE +
				   sizeof(struct layout_3_0) +
				   (sizeof(struct partition_3_0) * VDO_PARTITION_COUNT)),
	VDO_SUPER_BLOCK_FIXED_SIZE = VDO_ENCODED_HEADER_SIZE + sizeof(u32),
	VDO_MAX_COMPONENT_DATA_SIZE = VDO_SECTOR_SIZE - VDO_SUPER_BLOCK_FIXED_SIZE,
	VDO_COMPONENT_ENCODED_SIZE =
		(sizeof(struct packed_version_number) + sizeof(struct packed_vdo_component_41_0)),
	VDO_COMPONENT_DATA_OFFSET = VDO_ENCODED_HEADER_SIZE,
	VDO_COMPONENT_DATA_SIZE = (sizeof(u32) +
				   sizeof(struct packed_version_number) +
				   VDO_COMPONENT_ENCODED_SIZE +
				   VDO_LAYOUT_ENCODED_SIZE +
				   RECOVERY_JOURNAL_COMPONENT_ENCODED_SIZE +
				   SLAB_DEPOT_COMPONENT_ENCODED_SIZE +
				   BLOCK_MAP_COMPONENT_ENCODED_SIZE),
};

/* The entirety of the component data encoded in the VDO super block. */
struct vdo_component_states {
	/* For backwards compatibility */
	u32 unused;

	/* The VDO volume version */
	struct version_number volume_version;

	/* Components */
	struct vdo_component vdo;
	struct block_map_state_2_0 block_map;
	struct recovery_journal_state_7_0 recovery_journal;
	struct slab_depot_state_2_0 slab_depot;

	/* Our partitioning of the underlying storage */
	struct layout layout;
};

/**
 * vdo_are_same_version() - Check whether two version numbers are the same.
 * @version_a: The first version.
 * @version_b: The second version.
 *
 * Return: true if the two versions are the same.
 */
static inline bool vdo_are_same_version(struct version_number version_a,
					struct version_number version_b)
{
	return ((version_a.major_version == version_b.major_version) &&
		(version_a.minor_version == version_b.minor_version));
}

/**
 * vdo_is_upgradable_version() - Check whether an actual version is upgradable to an expected
 *                               version.
 * @expected_version: The expected version.
 * @actual_version: The version being validated.
 *
 * An actual version is upgradable if its major number is expected but its minor number differs,
 * and the expected version's minor number is greater than the actual version's minor number.
 *
 * Return: true if the actual version is upgradable.
 */
static inline bool vdo_is_upgradable_version(struct version_number expected_version,
					     struct version_number actual_version)
{
	return ((expected_version.major_version == actual_version.major_version) &&
		(expected_version.minor_version > actual_version.minor_version));
}

int __must_check vdo_validate_header(const struct header *expected_header,
				     const struct header *actual_header, bool exact_size,
				     const char *component_name);

void vdo_encode_header(u8 *buffer, size_t *offset, const struct header *header);
void vdo_decode_header(u8 *buffer, size_t *offset, struct header *header);

/**
 * vdo_pack_version_number() - Convert a version_number to its packed on-disk representation.
 * @version: The version number to convert.
 *
 * Return: the platform-independent representation of the version
 */
static inline struct packed_version_number vdo_pack_version_number(struct version_number version)
{
	return (struct packed_version_number) {
		.major_version = __cpu_to_le32(version.major_version),
		.minor_version = __cpu_to_le32(version.minor_version),
	};
}

/**
 * vdo_unpack_version_number() - Convert a packed_version_number to its native in-memory
 *                               representation.
 * @version: The version number to convert.
 *
 * Return: The platform-independent representation of the version.
 */
static inline struct version_number vdo_unpack_version_number(struct packed_version_number version)
{
	return (struct version_number) {
		.major_version = __le32_to_cpu(version.major_version),
		.minor_version = __le32_to_cpu(version.minor_version),
	};
}

/**
 * vdo_pack_header() - Convert a component header to its packed on-disk representation.
 * @header: The header to convert.
 *
 * Return: the platform-independent representation of the header
 */
static inline struct packed_header vdo_pack_header(const struct header *header)
{
	return (struct packed_header) {
		.id = __cpu_to_le32(header->id),
		.version = vdo_pack_version_number(header->version),
		.size = __cpu_to_le64(header->size),
	};
}

/**
 * vdo_unpack_header() - Convert a packed_header to its native in-memory representation.
 * @header: The header to convert.
 *
 * Return: The platform-independent representation of the version.
 */
static inline struct header vdo_unpack_header(const struct packed_header *header)
{
	return (struct header) {
		.id = __le32_to_cpu(header->id),
		.version = vdo_unpack_version_number(header->version),
		.size = __le64_to_cpu(header->size),
	};
}

/**
 * vdo_get_index_region_start() - Get the start of the index region from a geometry.
 * @geometry: The geometry.
 *
 * Return: The start of the index region.
 */
static inline physical_block_number_t __must_check
vdo_get_index_region_start(struct volume_geometry geometry)
{
	return geometry.regions[VDO_INDEX_REGION].start_block;
}

/**
 * vdo_get_data_region_start() - Get the start of the data region from a geometry.
 * @geometry: The geometry.
 *
 * Return: The start of the data region.
 */
static inline physical_block_number_t __must_check
vdo_get_data_region_start(struct volume_geometry geometry)
{
	return geometry.regions[VDO_DATA_REGION].start_block;
}

/**
 * vdo_get_index_region_size() - Get the size of the index region from a geometry.
 * @geometry: The geometry.
 *
 * Return: The size of the index region.
 */
static inline physical_block_number_t __must_check
vdo_get_index_region_size(struct volume_geometry geometry)
{
	return vdo_get_data_region_start(geometry) -
		vdo_get_index_region_start(geometry);
}

int __must_check vdo_parse_geometry_block(unsigned char *block,
					  struct volume_geometry *geometry);

static inline bool vdo_is_state_compressed(const enum block_mapping_state mapping_state)
{
	return (mapping_state > VDO_MAPPING_STATE_UNCOMPRESSED);
}

static inline struct block_map_entry
vdo_pack_block_map_entry(physical_block_number_t pbn, enum block_mapping_state mapping_state)
{
	return (struct block_map_entry) {
		.mapping_state = (mapping_state & 0x0F),
		.pbn_high_nibble = ((pbn >> 32) & 0x0F),
		.pbn_low_word = __cpu_to_le32(pbn & UINT_MAX),
	};
}

static inline struct data_location vdo_unpack_block_map_entry(const struct block_map_entry *entry)
{
	physical_block_number_t low32 = __le32_to_cpu(entry->pbn_low_word);
	physical_block_number_t high4 = entry->pbn_high_nibble;

	return (struct data_location) {
		.pbn = ((high4 << 32) | low32),
		.state = entry->mapping_state,
	};
}

static inline bool vdo_is_mapped_location(const struct data_location *location)
{
	return (location->state != VDO_MAPPING_STATE_UNMAPPED);
}

static inline bool vdo_is_valid_location(const struct data_location *location)
{
	if (location->pbn == VDO_ZERO_BLOCK)
		return !vdo_is_state_compressed(location->state);
	else
		return vdo_is_mapped_location(location);
}

static inline physical_block_number_t __must_check
vdo_get_block_map_page_pbn(const struct block_map_page *page)
{
	return __le64_to_cpu(page->header.pbn);
}

struct block_map_page *vdo_format_block_map_page(void *buffer, nonce_t nonce,
						 physical_block_number_t pbn,
						 bool initialized);

enum block_map_page_validity __must_check vdo_validate_block_map_page(struct block_map_page *page,
								      nonce_t nonce,
								      physical_block_number_t pbn);

static inline page_count_t vdo_compute_block_map_page_count(block_count_t entries)
{
	return DIV_ROUND_UP(entries, VDO_BLOCK_MAP_ENTRIES_PER_PAGE);
}

block_count_t __must_check vdo_compute_new_forest_pages(root_count_t root_count,
							struct boundary *old_sizes,
							block_count_t entries,
							struct boundary *new_sizes);

/**
 * vdo_pack_recovery_journal_entry() - Return the packed, on-disk representation of a recovery
 *                                     journal entry.
 * @entry: The journal entry to pack.
 *
 * Return: The packed representation of the journal entry.
 */
static inline struct packed_recovery_journal_entry
vdo_pack_recovery_journal_entry(const struct recovery_journal_entry *entry)
{
	return (struct packed_recovery_journal_entry) {
		.operation = entry->operation,
		.slot_low = entry->slot.slot & 0x3F,
		.slot_high = (entry->slot.slot >> 6) & 0x0F,
		.pbn_high_nibble = (entry->slot.pbn >> 32) & 0x0F,
		.pbn_low_word = __cpu_to_le32(entry->slot.pbn & UINT_MAX),
		.mapping = vdo_pack_block_map_entry(entry->mapping.pbn,
						    entry->mapping.state),
		.unmapping = vdo_pack_block_map_entry(entry->unmapping.pbn,
						      entry->unmapping.state),
	};
}

/**
 * vdo_unpack_recovery_journal_entry() - Unpack the on-disk representation of a recovery journal
 *                                       entry.
 * @entry: The recovery journal entry to unpack.
 *
 * Return: The unpacked entry.
 */
static inline struct recovery_journal_entry
vdo_unpack_recovery_journal_entry(const struct packed_recovery_journal_entry *entry)
{
	physical_block_number_t low32 = __le32_to_cpu(entry->pbn_low_word);
	physical_block_number_t high4 = entry->pbn_high_nibble;

	return (struct recovery_journal_entry) {
		.operation = entry->operation,
		.slot = {
			.pbn = ((high4 << 32) | low32),
			.slot = (entry->slot_low | (entry->slot_high << 6)),
		},
		.mapping = vdo_unpack_block_map_entry(&entry->mapping),
		.unmapping = vdo_unpack_block_map_entry(&entry->unmapping),
	};
}

const char * __must_check vdo_get_journal_operation_name(enum journal_operation operation);

/**
 * vdo_is_valid_recovery_journal_sector() - Determine whether the header of the given sector could
 *                                          describe a valid sector for the given journal block
 *                                          header.
 * @header: The unpacked block header to compare against.
 * @sector: The packed sector to check.
 * @sector_number: The number of the sector being checked.
 *
 * Return: true if the sector matches the block header.
 */
static inline bool __must_check
vdo_is_valid_recovery_journal_sector(const struct recovery_block_header *header,
				     const struct packed_journal_sector *sector,
				     u8 sector_number)
{
	if ((header->check_byte != sector->check_byte) ||
	    (header->recovery_count != sector->recovery_count))
		return false;

	if (header->metadata_type == VDO_METADATA_RECOVERY_JOURNAL_2)
		return sector->entry_count <= RECOVERY_JOURNAL_ENTRIES_PER_SECTOR;

	if (sector_number == 7)
		return sector->entry_count <= RECOVERY_JOURNAL_1_ENTRIES_IN_LAST_SECTOR;

	return sector->entry_count <= RECOVERY_JOURNAL_1_ENTRIES_PER_SECTOR;
}

/**
 * vdo_compute_recovery_journal_block_number() - Compute the physical block number of the recovery
 *                                               journal block which would have a given sequence
 *                                               number.
 * @journal_size: The size of the journal.
 * @sequence_number: The sequence number.
 *
 * Return: The pbn of the journal block which would the specified sequence number.
 */
static inline physical_block_number_t __must_check
vdo_compute_recovery_journal_block_number(block_count_t journal_size,
					  sequence_number_t sequence_number)
{
	/*
	 * Since journal size is a power of two, the block number modulus can just be extracted
	 * from the low-order bits of the sequence.
	 */
	return (sequence_number & (journal_size - 1));
}

/**
 * vdo_get_journal_block_sector() - Find the recovery journal sector from the block header and
 *                                  sector number.
 * @header: The header of the recovery journal block.
 * @sector_number: The index of the sector (1-based).
 *
 * Return: A packed recovery journal sector.
 */
static inline struct packed_journal_sector * __must_check
vdo_get_journal_block_sector(struct packed_journal_header *header, int sector_number)
{
	char *sector_data = ((char *) header) + (VDO_SECTOR_SIZE * sector_number);

	return (struct packed_journal_sector *) sector_data;
}

/**
 * vdo_pack_recovery_block_header() - Generate the packed representation of a recovery block
 *                                    header.
 * @header: The header containing the values to encode.
 * @packed: The header into which to pack the values.
 */
static inline void vdo_pack_recovery_block_header(const struct recovery_block_header *header,
						  struct packed_journal_header *packed)
{
	*packed = (struct packed_journal_header) {
		.block_map_head = __cpu_to_le64(header->block_map_head),
		.slab_journal_head = __cpu_to_le64(header->slab_journal_head),
		.sequence_number = __cpu_to_le64(header->sequence_number),
		.nonce = __cpu_to_le64(header->nonce),
		.logical_blocks_used = __cpu_to_le64(header->logical_blocks_used),
		.block_map_data_blocks = __cpu_to_le64(header->block_map_data_blocks),
		.entry_count = __cpu_to_le16(header->entry_count),
		.check_byte = header->check_byte,
		.recovery_count = header->recovery_count,
		.metadata_type = header->metadata_type,
	};
}

/**
 * vdo_unpack_recovery_block_header() - Decode the packed representation of a recovery block
 *                                      header.
 * @packed: The packed header to decode.
 *
 * Return: The unpacked header.
 */
static inline struct recovery_block_header
vdo_unpack_recovery_block_header(const struct packed_journal_header *packed)
{
	return (struct recovery_block_header) {
		.block_map_head = __le64_to_cpu(packed->block_map_head),
		.slab_journal_head = __le64_to_cpu(packed->slab_journal_head),
		.sequence_number = __le64_to_cpu(packed->sequence_number),
		.nonce = __le64_to_cpu(packed->nonce),
		.logical_blocks_used = __le64_to_cpu(packed->logical_blocks_used),
		.block_map_data_blocks = __le64_to_cpu(packed->block_map_data_blocks),
		.entry_count = __le16_to_cpu(packed->entry_count),
		.check_byte = packed->check_byte,
		.recovery_count = packed->recovery_count,
		.metadata_type = packed->metadata_type,
	};
}

/**
 * vdo_compute_slab_count() - Compute the number of slabs a depot with given parameters would have.
 * @first_block: PBN of the first data block.
 * @last_block: PBN of the last data block.
 * @slab_size_shift: Exponent for the number of blocks per slab.
 *
 * Return: The number of slabs.
 */
static inline slab_count_t vdo_compute_slab_count(physical_block_number_t first_block,
						  physical_block_number_t last_block,
						  unsigned int slab_size_shift)
{
	return (slab_count_t) ((last_block - first_block) >> slab_size_shift);
}

int __must_check vdo_configure_slab_depot(const struct partition *partition,
					  struct slab_config slab_config,
					  zone_count_t zone_count,
					  struct slab_depot_state_2_0 *state);

int __must_check vdo_configure_slab(block_count_t slab_size,
				    block_count_t slab_journal_blocks,
				    struct slab_config *slab_config);

/**
 * vdo_get_saved_reference_count_size() - Get the number of blocks required to save a reference
 *                                        counts state covering the specified number of data
 *                                        blocks.
 * @block_count: The number of physical data blocks that can be referenced.
 *
 * Return: The number of blocks required to save reference counts with the given block count.
 */
static inline block_count_t vdo_get_saved_reference_count_size(block_count_t block_count)
{
	return DIV_ROUND_UP(block_count, COUNTS_PER_BLOCK);
}

/**
 * vdo_get_slab_journal_start_block() - Get the physical block number of the start of the slab
 *                                      journal relative to the start block allocator partition.
 * @slab_config: The slab configuration of the VDO.
 * @origin: The first block of the slab.
 */
static inline physical_block_number_t __must_check
vdo_get_slab_journal_start_block(const struct slab_config *slab_config,
				 physical_block_number_t origin)
{
	return origin + slab_config->data_blocks + slab_config->reference_count_blocks;
}

/**
 * vdo_advance_journal_point() - Move the given journal point forward by one entry.
 * @point: The journal point to adjust.
 * @entries_per_block: The number of entries in one full block.
 */
static inline void vdo_advance_journal_point(struct journal_point *point,
					     journal_entry_count_t entries_per_block)
{
	point->entry_count++;
	if (point->entry_count == entries_per_block) {
		point->sequence_number++;
		point->entry_count = 0;
	}
}

/**
 * vdo_before_journal_point() - Check whether the first point precedes the second point.
 * @first: The first journal point.
 * @second: The second journal point.
 *
 * Return: true if the first point precedes the second point.
 */
static inline bool vdo_before_journal_point(const struct journal_point *first,
					    const struct journal_point *second)
{
	return ((first->sequence_number < second->sequence_number) ||
		((first->sequence_number == second->sequence_number) &&
		 (first->entry_count < second->entry_count)));
}

/**
 * vdo_pack_journal_point() - Encode the journal location represented by a
 *                            journal_point into a packed_journal_point.
 * @unpacked: The unpacked input point.
 * @packed: The packed output point.
 */
static inline void vdo_pack_journal_point(const struct journal_point *unpacked,
					  struct packed_journal_point *packed)
{
	packed->encoded_point =
		__cpu_to_le64((unpacked->sequence_number << 16) | unpacked->entry_count);
}

/**
 * vdo_unpack_journal_point() - Decode the journal location represented by a packed_journal_point
 *                              into a journal_point.
 * @packed: The packed input point.
 * @unpacked: The unpacked output point.
 */
static inline void vdo_unpack_journal_point(const struct packed_journal_point *packed,
					    struct journal_point *unpacked)
{
	u64 native = __le64_to_cpu(packed->encoded_point);

	unpacked->sequence_number = (native >> 16);
	unpacked->entry_count = (native & 0xffff);
}

/**
 * vdo_pack_slab_journal_block_header() - Generate the packed representation of a slab block
 *                                        header.
 * @header: The header containing the values to encode.
 * @packed: The header into which to pack the values.
 */
static inline void
vdo_pack_slab_journal_block_header(const struct slab_journal_block_header *header,
				   struct packed_slab_journal_block_header *packed)
{
	packed->head = __cpu_to_le64(header->head);
	packed->sequence_number = __cpu_to_le64(header->sequence_number);
	packed->nonce = __cpu_to_le64(header->nonce);
	packed->entry_count = __cpu_to_le16(header->entry_count);
	packed->metadata_type = header->metadata_type;
	packed->has_block_map_increments = header->has_block_map_increments;

	vdo_pack_journal_point(&header->recovery_point, &packed->recovery_point);
}

/**
 * vdo_unpack_slab_journal_block_header() - Decode the packed representation of a slab block
 *                                          header.
 * @packed: The packed header to decode.
 * @header: The header into which to unpack the values.
 */
static inline void
vdo_unpack_slab_journal_block_header(const struct packed_slab_journal_block_header *packed,
				     struct slab_journal_block_header *header)
{
	*header = (struct slab_journal_block_header) {
		.head = __le64_to_cpu(packed->head),
		.sequence_number = __le64_to_cpu(packed->sequence_number),
		.nonce = __le64_to_cpu(packed->nonce),
		.entry_count = __le16_to_cpu(packed->entry_count),
		.metadata_type = packed->metadata_type,
		.has_block_map_increments = packed->has_block_map_increments,
	};
	vdo_unpack_journal_point(&packed->recovery_point, &header->recovery_point);
}

/**
 * vdo_pack_slab_journal_entry() - Generate the packed encoding of a slab journal entry.
 * @packed: The entry into which to pack the values.
 * @sbn: The slab block number of the entry to encode.
 * @is_increment: The increment flag.
 */
static inline void vdo_pack_slab_journal_entry(packed_slab_journal_entry *packed,
					       slab_block_number sbn, bool is_increment)
{
	packed->offset_low8 = (sbn & 0x0000FF);
	packed->offset_mid8 = (sbn & 0x00FF00) >> 8;
	packed->offset_high7 = (sbn & 0x7F0000) >> 16;
	packed->increment = is_increment ? 1 : 0;
}

/**
 * vdo_unpack_slab_journal_entry() - Decode the packed representation of a slab journal entry.
 * @packed: The packed entry to decode.
 *
 * Return: The decoded slab journal entry.
 */
static inline struct slab_journal_entry __must_check
vdo_unpack_slab_journal_entry(const packed_slab_journal_entry *packed)
{
	struct slab_journal_entry entry;

	entry.sbn = packed->offset_high7;
	entry.sbn <<= 8;
	entry.sbn |= packed->offset_mid8;
	entry.sbn <<= 8;
	entry.sbn |= packed->offset_low8;
	entry.operation = VDO_JOURNAL_DATA_REMAPPING;
	entry.increment = packed->increment;
	return entry;
}

struct slab_journal_entry __must_check
vdo_decode_slab_journal_entry(struct packed_slab_journal_block *block,
			      journal_entry_count_t entry_count);

/**
 * vdo_get_slab_summary_hint_shift() - Compute the shift for slab summary hints.
 * @slab_size_shift: Exponent for the number of blocks per slab.
 *
 * Return: The hint shift.
 */
static inline u8 __must_check vdo_get_slab_summary_hint_shift(unsigned int slab_size_shift)
{
	return ((slab_size_shift > VDO_SLAB_SUMMARY_FULLNESS_HINT_BITS) ?
		(slab_size_shift - VDO_SLAB_SUMMARY_FULLNESS_HINT_BITS) :
		0);
}

int __must_check vdo_initialize_layout(block_count_t size,
				       physical_block_number_t offset,
				       block_count_t block_map_blocks,
				       block_count_t journal_blocks,
				       block_count_t summary_blocks,
				       struct layout *layout);

void vdo_uninitialize_layout(struct layout *layout);

int __must_check vdo_get_partition(struct layout *layout, enum partition_id id,
				   struct partition **partition_ptr);

struct partition * __must_check vdo_get_known_partition(struct layout *layout,
							enum partition_id id);

int vdo_validate_config(const struct vdo_config *config,
			block_count_t physical_block_count,
			block_count_t logical_block_count);

void vdo_destroy_component_states(struct vdo_component_states *states);

int __must_check vdo_decode_component_states(u8 *buffer,
					     struct volume_geometry *geometry,
					     struct vdo_component_states *states);

int __must_check vdo_validate_component_states(struct vdo_component_states *states,
					       nonce_t geometry_nonce,
					       block_count_t physical_size,
					       block_count_t logical_size);

void vdo_encode_super_block(u8 *buffer, struct vdo_component_states *states);
int __must_check vdo_decode_super_block(u8 *buffer);

/* We start with 0L and postcondition with ~0L to match our historical usage in userspace. */
static inline u32 vdo_crc32(const void *buf, unsigned long len)
{
	return (crc32(0L, buf, len) ^ ~0L);
}

#endif /* VDO_ENCODINGS_H */