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:
Zach Brown
2016-03-25 10:08:34 -07:00
parent 3bb00fafdc
commit 434cbb9c78
5 changed files with 90 additions and 0 deletions

View File

@@ -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) if (dentry->d_name.len > SCOUTFS_NAME_LEN)
return -ENAMETOOLONG; return -ENAMETOOLONG;
ret = scoutfs_dirty_inode_item(dir);
if (ret)
return ret;
inode = scoutfs_new_inode(sb, dir, mode, rdev); inode = scoutfs_new_inode(sb, dir, mode, rdev);
if (IS_ERR(inode)) if (IS_ERR(inode))
return PTR_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)) if (S_ISDIR(inode->i_mode) && i_size_read(inode))
return -ENOTEMPTY; 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); scoutfs_set_key(&key, scoutfs_ino(dir), SCOUTFS_DIRENT_KEY, di->hash);
ret = scoutfs_read_item(sb, &key, &ref); ret = scoutfs_read_item(sb, &key, &ref);

View File

@@ -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; 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 * 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 * item. This lets us write out blocks of items without having to track

View File

@@ -23,6 +23,7 @@ struct inode *scoutfs_alloc_inode(struct super_block *sb);
void scoutfs_destroy_inode(struct inode *inode); void scoutfs_destroy_inode(struct inode *inode);
struct inode *scoutfs_iget(struct super_block *sb, u64 ino); 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); void scoutfs_update_inode_item(struct inode *inode);
struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir, struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
umode_t mode, dev_t rdev); umode_t mode, dev_t rdev);

View File

@@ -475,6 +475,49 @@ out:
return ret; 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 * 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 * that are stored in dirty blocks. The caller is responsible for dropping

View File

@@ -22,6 +22,8 @@ int scoutfs_read_item(struct super_block *sb, struct scoutfs_key *key,
struct scoutfs_item_ref *ref); struct scoutfs_item_ref *ref);
int scoutfs_create_item(struct super_block *sb, struct scoutfs_key *key, int scoutfs_create_item(struct super_block *sb, struct scoutfs_key *key,
unsigned bytes, struct scoutfs_item_ref *ref); 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_delete_item(struct super_block *sb, struct scoutfs_item_ref *ref);
int scoutfs_next_item(struct super_block *sb, struct scoutfs_key *first, int scoutfs_next_item(struct super_block *sb, struct scoutfs_key *first,
struct scoutfs_key *last, struct list_head *iter_list, struct scoutfs_key *last, struct list_head *iter_list,