From 434cbb9c78b68b5709706a59f5e2dad047664792 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 25 Mar 2016 10:08:34 -0700 Subject: [PATCH] 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 --- kmod/src/dir.c | 9 +++++++++ kmod/src/inode.c | 35 +++++++++++++++++++++++++++++++++++ kmod/src/inode.h | 1 + kmod/src/segment.c | 43 +++++++++++++++++++++++++++++++++++++++++++ kmod/src/segment.h | 2 ++ 5 files changed, 90 insertions(+) diff --git a/kmod/src/dir.c b/kmod/src/dir.c index 50a39290..5a2902f5 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -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); diff --git a/kmod/src/inode.c b/kmod/src/inode.c index ba5eb8c4..a59cac5d 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -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 diff --git a/kmod/src/inode.h b/kmod/src/inode.h index 3da21640..650cd05b 100644 --- a/kmod/src/inode.h +++ b/kmod/src/inode.h @@ -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); diff --git a/kmod/src/segment.c b/kmod/src/segment.c index 9ea41977..4e94760a 100644 --- a/kmod/src/segment.c +++ b/kmod/src/segment.c @@ -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 diff --git a/kmod/src/segment.h b/kmod/src/segment.h index 6b41579b..a990d422 100644 --- a/kmod/src/segment.h +++ b/kmod/src/segment.h @@ -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,