From aa7090315415087d75e197251b4bf135715c2a2a Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 29 Sep 2017 09:50:08 -0700 Subject: [PATCH] scoutfs: add lock coverage for data paths Use per_task storage on the inode to pass locks from high level read and write lock holders down into the callbacks that operate under the locks so that the locks can then be passed to the item functions. Signed-off-by: Zach Brown --- kmod/src/data.c | 28 ++++++++++++++++++++++------ kmod/src/file.c | 11 +++++++++++ kmod/src/inode.c | 1 + kmod/src/inode.h | 2 ++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/kmod/src/data.c b/kmod/src/data.c index 8c249acd..05083e8b 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -908,7 +908,8 @@ out: */ static int find_alloc_block(struct super_block *sb, struct block_mapping *map, struct scoutfs_key_buf *map_key, - unsigned map_ind, bool map_exists) + unsigned map_ind, bool map_exists, + struct scoutfs_lock *data_lock) { DECLARE_DATA_INFO(sb, datinf); struct task_cursor *curs; @@ -958,7 +959,7 @@ static int find_alloc_block(struct super_block *sb, struct block_mapping *map, /* ensure that we can copy in encoded without failing */ scoutfs_kvec_init(val, map->encoded, sizeof(map->encoded)); if (map_exists) - ret = scoutfs_item_update(sb, map_key, val, NULL); + ret = scoutfs_item_update(sb, map_key, val, data_lock->end); else ret = scoutfs_item_create(sb, map_key, val); if (ret) @@ -1000,6 +1001,7 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock, struct super_block *sb = inode->i_sb; struct scoutfs_block_mapping_key bmk; struct scoutfs_key_buf key; + struct scoutfs_lock *lock; struct block_mapping *map; SCOUTFS_DECLARE_KVEC(val); bool exists; @@ -1007,6 +1009,10 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock, int ret; int i; + lock = scoutfs_per_task_get(&si->pt_data_lock); + if (WARN_ON_ONCE(!lock)) + return -EINVAL; + map = kmalloc(sizeof(struct block_mapping), GFP_NOFS); if (!map) return -ENOMEM; @@ -1015,7 +1021,7 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock, scoutfs_kvec_init(val, map->encoded, sizeof(map->encoded)); /* find the mapping item that covers the logical block */ - ret = scoutfs_item_lookup(sb, &key, val, NULL); + ret = scoutfs_item_lookup(sb, &key, val, lock); if (ret < 0) { if (ret != -ENOENT) goto out; @@ -1044,7 +1050,7 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock, * and try again if we've already done a bulk alloc in * our transaction. */ - ret = find_alloc_block(sb, map, &key, ind, exists); + ret = find_alloc_block(sb, map, &key, ind, exists, lock); if (ret) goto out; } @@ -1133,11 +1139,17 @@ static int scoutfs_write_begin(struct file *file, struct page **pagep, void **fsdata) { struct inode *inode = mapping->host; + struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; + struct scoutfs_lock *lock; int ret; trace_scoutfs_write_begin(sb, scoutfs_ino(inode), (__u64)pos, len); + lock = scoutfs_per_task_get(&si->pt_data_lock); + if (WARN_ON_ONCE(!lock)) + return -EINVAL; + ret = scoutfs_hold_trans(sb, SIC_WRITE_BEGIN()); if (ret) goto out; @@ -1146,7 +1158,7 @@ static int scoutfs_write_begin(struct file *file, flags |= AOP_FLAG_NOFS; /* generic write_end updates i_size and calls dirty_inode */ - ret = scoutfs_dirty_inode_item(inode, NULL); + ret = scoutfs_dirty_inode_item(inode, lock); if (ret == 0) ret = block_write_begin(mapping, pos, len, flags, pagep, scoutfs_get_block); @@ -1163,11 +1175,15 @@ static int scoutfs_write_end(struct file *file, struct address_space *mapping, struct inode *inode = mapping->host; struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; + struct scoutfs_lock *lock; int ret; trace_scoutfs_write_end(sb, scoutfs_ino(inode), page->index, (u64)pos, len, copied); + /* always call write_end, update_inode will bark if there's no lock */ + lock = scoutfs_per_task_get(&si->pt_data_lock); + ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); if (ret > 0) { if (!si->staging) { @@ -1175,7 +1191,7 @@ static int scoutfs_write_end(struct file *file, struct address_space *mapping, scoutfs_inode_inc_data_version(inode); } /* XXX kind of a big hammer, inode life cycle needs work */ - scoutfs_update_inode_item(inode, NULL); + scoutfs_update_inode_item(inode, lock); scoutfs_inode_queue_writeback(inode); } scoutfs_release_trans(sb); diff --git a/kmod/src/file.c b/kmod/src/file.c index 2878d61e..b59df5e0 100644 --- a/kmod/src/file.c +++ b/kmod/src/file.c @@ -26,6 +26,8 @@ #include "item.h" #include "lock.h" #include "file.h" +#include "inode.h" +#include "per_task.h" /* TODO: Direct I/O, AIO */ ssize_t scoutfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov, @@ -33,14 +35,18 @@ ssize_t scoutfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov, { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); + struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; struct scoutfs_lock *inode_lock = NULL; + SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); int ret; ret = scoutfs_lock_inode(sb, DLM_LOCK_PR, SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock); if (ret == 0) { + scoutfs_per_task_add(&si->pt_data_lock, &pt_ent, inode_lock); ret = generic_file_aio_read(iocb, iov, nr_segs, pos); + scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); scoutfs_unlock(sb, inode_lock, DLM_LOCK_PR); } @@ -52,8 +58,10 @@ ssize_t scoutfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); + struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; struct scoutfs_lock *inode_lock = NULL; + SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); int ret; if (iocb->ki_left == 0) /* Does this even happen? */ @@ -65,10 +73,13 @@ ssize_t scoutfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (ret) goto out; + scoutfs_per_task_add(&si->pt_data_lock, &pt_ent, inode_lock); + /* XXX: remove SUID bit */ ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos); + scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); scoutfs_unlock(sb, inode_lock, DLM_LOCK_EX); out: mutex_unlock(&inode->i_mutex); diff --git a/kmod/src/inode.c b/kmod/src/inode.c index 4b144faa..08e7b31e 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -73,6 +73,7 @@ static void scoutfs_inode_ctor(void *obj) mutex_init(&ci->item_mutex); seqcount_init(&ci->seqcount); ci->staging = false; + scoutfs_per_task_init(&ci->pt_data_lock); init_rwsem(&ci->xattr_rwsem); RB_CLEAR_NODE(&ci->writeback_node); diff --git a/kmod/src/inode.h b/kmod/src/inode.h index f590fdfb..dc9a2e60 100644 --- a/kmod/src/inode.h +++ b/kmod/src/inode.h @@ -3,6 +3,7 @@ #include "key.h" #include "lock.h" +#include "per_task.h" struct scoutfs_lock; @@ -34,6 +35,7 @@ struct scoutfs_inode_info { /* initialized once for slab object */ seqcount_t seqcount; bool staging; /* holder of i_mutex is staging */ + struct scoutfs_per_task pt_data_lock; struct rw_semaphore xattr_rwsem; struct rb_node writeback_node;