/* Core filesystem: handling of regular files and caching of file data. */
/* Mark a given block dirty and make sure that data is faulted in so that it
- * can be written to. */
-void bluesky_block_touch(BlueSkyInode *inode, uint64_t i)
+ * can be written to.
+ *
+ * If preserve is set to false, this is a hint that the block is about to be
+ * entirely overwritten. In this case, a dirty block is made available but any
+ * prior contents might be lost. A value of preserve = TRUE is always safe. */
+void bluesky_block_touch(BlueSkyInode *inode, uint64_t i, gboolean preserve)
{
g_return_if_fail(i < inode->blocks->len);
BlueSkyBlock *block = &g_array_index(inode->blocks, BlueSkyBlock, i);
block->dirty = bluesky_string_new(g_malloc0(block_len), block_len);
break;
case BLUESKY_BLOCK_REF:
- // FIXME: locking on the cloudlog?
- bluesky_block_fetch(inode, block, NULL);
- bluesky_string_ref(block->ref->data);
- block->dirty = bluesky_string_dup(block->ref->data);
+ if (preserve) {
+ // FIXME: locking on the cloudlog?
+ bluesky_block_fetch(inode, block, NULL);
+ bluesky_string_ref(block->ref->data);
+ block->dirty = bluesky_string_dup(block->ref->data);
+ } else {
+ block->dirty = bluesky_string_new(g_malloc0(block_len), block_len);
+ }
break;
case BLUESKY_BLOCK_DIRTY:
block->dirty = bluesky_string_dup(block->dirty);
uint64_t blocks = (size + BLUESKY_BLOCK_SIZE - 1) / BLUESKY_BLOCK_SIZE;
+ /* Calculate number of bytes in the last block of the file */
+ int lastblock_old, lastblock_new;
+ lastblock_old = inode->size % BLUESKY_BLOCK_SIZE;
+ if (lastblock_old == 0 && inode->size > 0)
+ lastblock_old = BLUESKY_BLOCK_SIZE;
+ lastblock_new = size % BLUESKY_BLOCK_SIZE;
+ if (lastblock_new == 0 && size > 0)
+ lastblock_new = BLUESKY_BLOCK_SIZE;
+
if (blocks > inode->blocks->len) {
/* Need to add new blocks to the end of a file. New block structures
* are automatically zeroed, which initializes them to be pointers to
inode->blocks->len - 1);
if (b->type != BLUESKY_BLOCK_ZERO
- && (b->type == BLUESKY_BLOCK_REF
- || b->dirty->len < BLUESKY_BLOCK_SIZE)) {
- bluesky_block_touch(inode, inode->blocks->len - 1);
+ && lastblock_old < BLUESKY_BLOCK_SIZE) {
+ bluesky_block_touch(inode, inode->blocks->len - 1, TRUE);
gsize old_size = b->dirty->len;
+ g_warn_if_fail(lastblock_old != old_size);
bluesky_string_resize(b->dirty, BLUESKY_BLOCK_SIZE);
memset(&b->dirty->data[old_size], 0,
BLUESKY_BLOCK_SIZE - old_size);
BlueSkyBlock *b = &g_array_index(inode->blocks, BlueSkyBlock,
blocks - 1);
- if (b->type != BLUESKY_BLOCK_ZERO) {
- bluesky_block_touch(inode, blocks - 1);
+ gboolean need_resize = TRUE;
+ if (b->type == BLUESKY_BLOCK_ZERO)
+ need_resize = FALSE;
+ else if (size < inode->size && lastblock_new == BLUESKY_BLOCK_SIZE)
+ need_resize = FALSE;
+
+ if (need_resize) {
+ bluesky_block_touch(inode, blocks - 1, TRUE);
gsize old_size = b->dirty->len;
gsize new_size = size - (blocks - 1) * BLUESKY_BLOCK_SIZE;
return;
// TODO: Optimization: If we are entirely overwriting a block we don't need
- // to fetch it frm storage first.
+ // to fetch it frm storage first. We don't yet handle the case where the
+ // partial last block of a file is entirely overwritten.
while (len > 0) {
uint64_t block_num = offset / BLUESKY_BLOCK_SIZE;
gint block_offset = offset % BLUESKY_BLOCK_SIZE;
gint bytes = MIN(BLUESKY_BLOCK_SIZE - block_offset, len);
- bluesky_block_touch(inode, block_num);
+ gboolean preserve = TRUE;
+ if (block_offset == 0 && bytes == BLUESKY_BLOCK_SIZE) {
+ preserve = FALSE;
+ }
+ bluesky_block_touch(inode, block_num, preserve);
BlueSkyBlock *b = &g_array_index(inode->blocks, BlueSkyBlock,
block_num);
memcpy(&b->dirty->data[block_offset], data, bytes);
g_return_if_fail(offset < inode->size);
g_return_if_fail(len <= inode->size - offset);
-#if 0
- /* Start fetches on any data blocks that we will need for this read. */
- BlueSkyStoreAsync *barrier = bluesky_store_async_new(inode->fs->store);
- barrier->op = STORE_OP_BARRIER;
+ BlueSkyProfile *profile = bluesky_profile_get();
+
+ bluesky_profile_add_event(profile,
+ g_strdup_printf("Start file read prefetch"));
uint64_t start_block, end_block;
start_block = offset / BLUESKY_BLOCK_SIZE;
end_block = (offset + len - 1) / BLUESKY_BLOCK_SIZE;
- if (bluesky_verbose) {
- g_log("bluesky/file", G_LOG_LEVEL_DEBUG,
- "Start prefetch on blocks %"PRIi64" .. %"PRIi64,
- start_block, end_block);
- }
for (uint64_t i = start_block; i <= end_block; i++) {
BlueSkyBlock *b = &g_array_index(inode->blocks, BlueSkyBlock,
i);
if (b->type == BLUESKY_BLOCK_REF)
- bluesky_block_fetch(inode, b, barrier);
+ bluesky_cloudlog_prefetch(b->ref);
}
- bluesky_store_async_submit(barrier);
- bluesky_store_async_wait(barrier);
- bluesky_store_async_unref(barrier);
- if (bluesky_verbose) {
- g_log("bluesky/file", G_LOG_LEVEL_DEBUG, "Prefetch complete.");
- }
-#endif
+
+ bluesky_profile_add_event(profile,
+ g_strdup_printf("End file read prefetch"));
while (len > 0) {
uint64_t block_num = offset / BLUESKY_BLOCK_SIZE;
buf += bytes;
len -= bytes;
}
+
+ bluesky_profile_add_event(profile,
+ g_strdup_printf("BlueSky read complete"));
}
void bluesky_block_fetch(BlueSkyInode *inode, BlueSkyBlock *block,