KKASSERT(data_len >= 0 && data_len <= HAMMER_XBUFSIZE);
cursor->data = hammer_bread_ext(hmp, data_off, data_len,
&error, &cursor->data_buffer);
- if (hammer_crc_test_leaf(cursor->data, &elm->leaf) == 0) {
+ if (error == 0 &&
+ hammer_crc_test_leaf(cursor->data, &elm->leaf) == 0) {
kprintf("CRC DATA @ %016llx/%d FAILED\n",
(long long)elm->leaf.data_offset, elm->leaf.data_len);
if (hammer_debug_critical)
* BUFFERS *
************************************************************************
*
- * Manage buffers. Currently all blockmap-backed zones are direct-mapped
+ * Manage buffers. Currently most blockmap-backed zones are direct-mapped
* to zone-2 buffer offsets, without a translation stage. However, the
* hammer_buffer structure is indexed by its zoneX_offset, not its
* zone2_offset.
* Insert the buffer into the RB tree and handle late collisions.
*/
if (RB_INSERT(hammer_buf_rb_tree, &hmp->rb_bufs_root, buffer)) {
- hammer_unref(&buffer->io.lock); /* safety */
- --hammer_count_buffers;
hammer_rel_volume(volume, 0);
buffer->io.volume = NULL; /* safety */
+ hammer_unref(&buffer->io.lock); /* safety */
+ --hammer_count_buffers;
kfree(buffer, hmp->m_misc);
goto again;
}
if (*errorp) {
hammer_rel_buffer(buffer, 1);
buffer = NULL;
+ } else {
+ hammer_io_advance(&buffer->io);
}
} else {
*errorp = 0;
+ hammer_io_advance(&buffer->io);
}
- hammer_io_advance(&buffer->io);
return(buffer);
}
/*
* Flush recovered buffers from recovery operations. The call to this
* routine may be delayed if a read-only mount was made and then later
- * upgraded to read-write.
+ * upgraded to read-write. This routine is also called when unmounting
+ * a read-only mount to clean out recovered (dirty) buffers which we
+ * couldn't flush (because the mount is read-only).
*
* The volume header is always written last. The UNDO FIFO will be forced
* to zero-length by setting next_offset to first_offset. This leaves the
* all volume headers (including the root volume) will be discarded.
* Otherwise data is the root_volume and we flush all volume headers
* EXCEPT the root_volume.
+ *
+ * Clear any I/O error or modified condition when discarding buffers to
+ * clean up the reference count, otherwise the buffer may have extra refs
+ * on it.
*/
static
int
if (volume->io.recovered && volume != root_volume) {
volume->io.recovered = 0;
- if (root_volume != NULL)
+ if (root_volume != NULL) {
hammer_io_flush(&volume->io, 0);
- else
+ } else {
+ hammer_io_clear_error(&volume->io);
hammer_io_clear_modify(&volume->io, 1);
+ }
hammer_rel_volume(volume, 0);
}
return(0);
}
+/*
+ * Flush or discard recovered I/O buffers.
+ *
+ * Clear any I/O error or modified condition when discarding buffers to
+ * clean up the reference count, otherwise the buffer may have extra refs
+ * on it.
+ */
static
int
hammer_recover_flush_buffer_callback(hammer_buffer_t buffer, void *data)
if (buffer->io.recovered) {
buffer->io.recovered = 0;
buffer->io.reclaim = 1;
- if (final < 0)
+ if (final < 0) {
+ hammer_io_clear_error(&buffer->io);
hammer_io_clear_modify(&buffer->io, 1);
- else
+ } else {
hammer_io_flush(&buffer->io, 0);
+ }
hammer_rel_buffer(buffer, 0);
} else {
- KKASSERT(buffer->io.lock.refs == 0);
- ++hammer_count_refedbufs;
+ if (buffer->io.lock.refs == 0)
+ ++hammer_count_refedbufs;
hammer_ref(&buffer->io.lock);
+ if (final < 0) {
+ hammer_io_clear_error(&buffer->io);
+ hammer_io_clear_modify(&buffer->io, 1);
+ }
+ KKASSERT(buffer->io.lock.refs == 1);
buffer->io.reclaim = 1;
hammer_rel_buffer(buffer, 1);
}