mirror of
https://github.com/versity/scoutfs.git
synced 2025-12-23 13:35:18 +00:00
scoutfs: create dirty items for inode updates
Inode updates weren't persistent because they were being stored in clean segments in memory. This was triggered by the new hashed dirent mechanism returning -ENOENT when the inode still had a 0 max dirent hash nr. We make sure that there is a dirty item in the dirty segment at the start of inode modification so that later updates will store in the dirty segment. Nothing ensures that the dirty segment won't be written out today but that will be added soon. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -324,6 +324,10 @@ static int scoutfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
if (dentry->d_name.len > SCOUTFS_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
ret = scoutfs_dirty_inode_item(dir);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
inode = scoutfs_new_inode(sb, dir, mode, rdev);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
@@ -408,6 +412,11 @@ static int scoutfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
if (S_ISDIR(inode->i_mode) && i_size_read(inode))
|
||||
return -ENOTEMPTY;
|
||||
|
||||
ret = scoutfs_dirty_inode_item(dir) ?:
|
||||
scoutfs_dirty_inode_item(inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scoutfs_set_key(&key, scoutfs_ino(dir), SCOUTFS_DIRENT_KEY, di->hash);
|
||||
|
||||
ret = scoutfs_read_item(sb, &key, &ref);
|
||||
|
||||
@@ -190,6 +190,41 @@ static void store_inode(struct scoutfs_inode *cinode, struct inode *inode)
|
||||
cinode->max_dirent_hash_nr = ci->max_dirent_hash_nr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a pinned dirty inode item so that we can later update the
|
||||
* inode item without risking failure. We often wouldn't want to have
|
||||
* to unwind inode modifcations (perhaps by shared vfs code!) if our
|
||||
* item update failed. This is our chance to return errors for enospc
|
||||
* for lack of space for new logged dirty inode items.
|
||||
*
|
||||
* This dirty inode item will be found by lookups in the interim so we
|
||||
* have to update it now with the current inode contents.
|
||||
*
|
||||
* Callers don't delete these dirty items on errors. They're still
|
||||
* valid and will be merged with the current item eventually. They can
|
||||
* be found in the dirty block to avoid future dirtying (say repeated
|
||||
* creations in a directory).
|
||||
*
|
||||
* The caller has to prevent sync between dirtying and updating the
|
||||
* inodes.
|
||||
*/
|
||||
int scoutfs_dirty_inode_item(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
DECLARE_SCOUTFS_ITEM_REF(ref);
|
||||
struct scoutfs_key key;
|
||||
int ret;
|
||||
|
||||
scoutfs_set_key(&key, scoutfs_ino(inode), SCOUTFS_INODE_KEY, 0);
|
||||
|
||||
ret = scoutfs_dirty_item(sb, &key, sizeof(struct scoutfs_inode), &ref);
|
||||
if (!ret) {
|
||||
store_inode(ref.val, inode);
|
||||
scoutfs_put_ref(&ref);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Every time we modify the inode in memory we copy it to its inode
|
||||
* item. This lets us write out blocks of items without having to track
|
||||
|
||||
@@ -23,6 +23,7 @@ struct inode *scoutfs_alloc_inode(struct super_block *sb);
|
||||
void scoutfs_destroy_inode(struct inode *inode);
|
||||
|
||||
struct inode *scoutfs_iget(struct super_block *sb, u64 ino);
|
||||
int scoutfs_dirty_inode_item(struct inode *inode);
|
||||
void scoutfs_update_inode_item(struct inode *inode);
|
||||
struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
|
||||
umode_t mode, dev_t rdev);
|
||||
|
||||
@@ -475,6 +475,49 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that there is a dirty item with the given key in the current
|
||||
* dirty segment.
|
||||
*
|
||||
* The caller locks access to the item and prevents sync and made sure
|
||||
* that there's enough free space in the segment for their dirty inodes.
|
||||
*
|
||||
* This is better than getting -EEXIST from create_item because that
|
||||
* will leave the allocated item and val dangling in the block when it
|
||||
* returns the error.
|
||||
*/
|
||||
int scoutfs_dirty_item(struct super_block *sb, struct scoutfs_key *key,
|
||||
unsigned bytes, struct scoutfs_item_ref *ref)
|
||||
{
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
struct scoutfs_item *item;
|
||||
struct buffer_head *bh;
|
||||
bool create = false;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sbi->dirty_mutex);
|
||||
|
||||
if (sbi->dirty_blkno) {
|
||||
ret = scoutfs_skip_lookup(sb, sbi->dirty_blkno, key, &bh,
|
||||
&item);
|
||||
if (ret == -ENOENT)
|
||||
create = true;
|
||||
else if (!ret) {
|
||||
ret = populate_ref(sb, sbi->dirty_blkno, bh, item,
|
||||
ref);
|
||||
brelse(bh);
|
||||
}
|
||||
} else {
|
||||
create = true;
|
||||
}
|
||||
mutex_unlock(&sbi->dirty_mutex);
|
||||
|
||||
if (create)
|
||||
ret = scoutfs_create_item(sb, key, bytes, ref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a really cheesy temporary delete method. It only works on items
|
||||
* that are stored in dirty blocks. The caller is responsible for dropping
|
||||
|
||||
@@ -22,6 +22,8 @@ int scoutfs_read_item(struct super_block *sb, struct scoutfs_key *key,
|
||||
struct scoutfs_item_ref *ref);
|
||||
int scoutfs_create_item(struct super_block *sb, struct scoutfs_key *key,
|
||||
unsigned bytes, struct scoutfs_item_ref *ref);
|
||||
int scoutfs_dirty_item(struct super_block *sb, struct scoutfs_key *key,
|
||||
unsigned bytes, struct scoutfs_item_ref *ref);
|
||||
int scoutfs_delete_item(struct super_block *sb, struct scoutfs_item_ref *ref);
|
||||
int scoutfs_next_item(struct super_block *sb, struct scoutfs_key *first,
|
||||
struct scoutfs_key *last, struct list_head *iter_list,
|
||||
|
||||
Reference in New Issue
Block a user