scoutfs: more carefully manage private bh bits

The management of _checked and _valid_crc private bits in the
buffer_head wasn't quite right.

_checked indicates that the block has been checked and that the
expensive crc verification doesn't need to be recalculated.  _valid_crc
then indicates the result of the crc verification.

_checked is read without locks.  First, we didn't make sure that
_valid_crc was stored before _checked.  Multiple tasks could race to see
_checked before _valid_crc.  So we add some memory barriers.

Then we didn't clear _checked when re-reading a stale block.  This meant
that the moment the block was read its private flags could still
indicate that it had a valid crc.  We clear the private bits before we
read so that we'll recalculate the crc.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2018-04-26 15:06:46 -07:00
committed by Zach Brown
parent fe8b155061
commit 7d7f8e45b7

View File

@@ -611,6 +611,7 @@ static bool valid_referenced_block(struct scoutfs_super_block *super,
__le32 existing;
u32 calc;
smp_rmb(); /* load checked before crc */
if (!buffer_scoutfs_checked(bh)) {
lock_buffer(bh);
if (!buffer_scoutfs_checked(bh)) {
@@ -619,11 +620,13 @@ static bool valid_referenced_block(struct scoutfs_super_block *super,
calc = crc32c(~0, bt, SCOUTFS_BLOCK_SIZE);
bt->crc = existing;
set_buffer_scoutfs_checked(bh);
if (calc == le32_to_cpu(existing))
set_buffer_scoutfs_valid_crc(bh);
else
clear_buffer_scoutfs_valid_crc(bh);
smp_wmb(); /* store crc before checked */
set_buffer_scoutfs_checked(bh);
}
unlock_buffer(bh);
}
@@ -683,6 +686,9 @@ retry:
lock_buffer(bh);
clear_buffer_uptodate(bh);
clear_buffer_scoutfs_valid_crc(bh);
smp_wmb(); /* store crc before checked */
clear_buffer_scoutfs_checked(bh);
unlock_buffer(bh);
put_bh(bh);
bt = NULL;