nvdimm(4): Fix various problems when the using the second label index block

struct nvdimm_label_index is dynamically sized, with the `free`
bitfield expanding to hold `slot_cnt` entries. Fix a few places
where we were treating the struct as though it had a fixed sized.

Reviewed by:	cem
Approved by:	scottl (mentor)
MFC after:	1 week
Sponsored by:	Intel Corporation
Differential Revision:	https://reviews.freebsd.org/D22253
This commit is contained in:
scottph 2019-11-12 16:24:37 +00:00
parent 7281972388
commit f246ece7eb

View File

@ -183,7 +183,7 @@ label_index_is_valid(struct nvdimm_label_index *index, uint32_t max_labels,
{
uint64_t checksum;
index = (struct nvdimm_label_index *)((uint8_t *)index + offset);
index = (struct nvdimm_label_index *)((uint8_t *)index + size * offset);
if (strcmp(index->signature, NVDIMM_INDEX_BLOCK_SIGNATURE) != 0)
return false;
checksum = index->checksum;
@ -242,7 +242,7 @@ read_label(struct nvdimm_dev *nv, int num)
static int
read_labels(struct nvdimm_dev *nv)
{
struct nvdimm_label_index *indices;
struct nvdimm_label_index *indices, *index1;
size_t bitfield_size, index_size, num_labels;
int error, n;
bool index_0_valid, index_1_valid;
@ -258,6 +258,7 @@ read_labels(struct nvdimm_dev *nv)
sizeof(struct nvdimm_label);
bitfield_size = roundup2(num_labels, 8) / 8;
indices = malloc(2 * index_size, M_NVDIMM, M_WAITOK);
index1 = (void *)((uint8_t *)indices + index_size);
error = read_label_area(nv, (void *)indices, 0, 2 * index_size);
if (error != 0) {
free(indices, M_NVDIMM);
@ -271,18 +272,29 @@ read_labels(struct nvdimm_dev *nv)
free(indices, M_NVDIMM);
return (ENXIO);
}
if (index_0_valid && index_1_valid &&
(indices[1].seq > indices[0].seq ||
(indices[1].seq == 1 && indices[0].seq == 3)))
index_0_valid = false;
if (index_0_valid && index_1_valid) {
if (((int)indices->seq - (int)index1->seq + 3) % 3 == 1) {
/* index 0 was more recently updated */
index_1_valid = false;
} else {
/*
* either index 1 was more recently updated,
* or the sequence numbers are equal, in which
* case the specification says the block with
* the higher offset is to be treated as valid
*/
index_0_valid = false;
}
}
nv->label_index = malloc(index_size, M_NVDIMM, M_WAITOK);
bcopy(indices + (index_0_valid ? 0 : 1), nv->label_index, index_size);
bcopy(index_0_valid ? indices : index1, nv->label_index, index_size);
free(indices, M_NVDIMM);
for (bit_ffc_at((bitstr_t *)nv->label_index->free, 0, num_labels, &n);
n >= 0;
bit_ffc_at((bitstr_t *)nv->label_index->free, n + 1, num_labels,
&n)) {
bit_ffc_at((bitstr_t *)nv->label_index->free, 0,
nv->label_index->slot_cnt, &n);
while (n >= 0) {
read_label(nv, n);
bit_ffc_at((bitstr_t *)nv->label_index->free, n + 1,
nv->label_index->slot_cnt, &n);
}
return (0);
}