diff --git a/kmod/src/count.h b/kmod/src/count.h index 0135fb81..41817ea7 100644 --- a/kmod/src/count.h +++ b/kmod/src/count.h @@ -117,6 +117,24 @@ static inline const struct scoutfs_item_count SIC_MKNOD(unsigned name_len) return cnt; } +/* + * Dropping the inode deletes all its items. Potentially enormous numbers + * of items (data mapping, xattrs) are deleted in their own transactions. + */ +static inline const struct scoutfs_item_count SIC_DROP_INODE(int mode, + u64 size) +{ + struct scoutfs_item_count cnt = {0,}; + + if (S_ISLNK(mode)) + __count_sym_target(&cnt, size); + __count_dirty_inode(&cnt); + __count_orphan(&cnt); + + cnt.vals = 0; + return cnt; +} + static inline const struct scoutfs_item_count SIC_LINK(unsigned name_len) { struct scoutfs_item_count cnt = {0,}; diff --git a/kmod/src/inode.c b/kmod/src/inode.c index 20676cee..55400260 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -1400,6 +1400,7 @@ static int delete_inode_items(struct super_block *sb, u64 ino) struct kvec val; umode_t mode; u64 ind_seq; + u64 size; int ret; ret = scoutfs_lock_ino(sb, DLM_LOCK_EX, 0, ino, &lock); @@ -1424,14 +1425,27 @@ static int delete_inode_items(struct super_block *sb, u64 ino) } mode = le32_to_cpu(sinode.mode); - trace_scoutfs_delete_inode(sb, ino, mode); + size = le64_to_cpu(sinode.size); + trace_scoutfs_delete_inode(sb, ino, mode, size); - /* XXX the trans reservation count is obviously bonkers :) */ + /* remove data items in their own transactions */ + if (S_ISREG(mode)) { + ret = scoutfs_data_truncate_items(sb, NULL, ino, 0, ~0ULL, + false, lock); + if (ret) + goto out; + } + + ret = scoutfs_xattr_drop(sb, ino, lock); + if (ret) + goto out; + + /* then delete the small known number of remaining inode items */ retry: ret = scoutfs_inode_index_start(sb, &ind_seq) ?: prepare_index_deletion(sb, &ind_locks, ino, mode, &sinode) ?: scoutfs_inode_index_try_lock_hold(sb, &ind_locks, ind_seq, - SIC_DIRTY_INODE()); + SIC_DROP_INODE(mode, size)); if (ret > 0) goto retry; if (ret) @@ -1439,24 +1453,16 @@ retry: release = true; - /* first remove index items to try to avoid indexing partial deletion */ ret = remove_index_items(sb, ino, &sinode, &ind_locks); if (ret) goto out; -#if 0 - ret = scoutfs_xattr_drop(sb, ino); - if (ret) - goto out; + if (S_ISLNK(mode)) { + ret = scoutfs_symlink_drop(sb, ino, lock, size); + if (ret) + goto out; + } - if (S_ISLNK(mode)) - ret = scoutfs_symlink_drop(sb, ino, i_size); - else if (S_ISREG(mode)) - ret = scoutfs_truncate_extent_items(sb, ino, 0, ~0ULL, false); - if (ret) - goto out; - -#endif ret = scoutfs_item_delete(sb, &key, lock); if (ret) goto out; diff --git a/kmod/src/scoutfs_trace.h b/kmod/src/scoutfs_trace.h index 9d32367b..fca0b99d 100644 --- a/kmod/src/scoutfs_trace.h +++ b/kmod/src/scoutfs_trace.h @@ -1296,24 +1296,27 @@ TRACE_EVENT(scoutfs_orphan_inode, ); TRACE_EVENT(scoutfs_delete_inode, - TP_PROTO(struct super_block *sb, u64 ino, umode_t mode), + TP_PROTO(struct super_block *sb, u64 ino, umode_t mode, u64 size), - TP_ARGS(sb, ino, mode), + TP_ARGS(sb, ino, mode, size), TP_STRUCT__entry( __field(dev_t, dev) __field(__u64, ino) __field(umode_t, mode) + __field(__u64, size) ), TP_fast_assign( __entry->dev = sb->s_dev; __entry->ino = ino; __entry->mode = mode; + __entry->size = size; ), - TP_printk("dev %d,%d ino %llu, mode 0x%x", MAJOR(__entry->dev), - MINOR(__entry->dev), __entry->ino, __entry->mode) + TP_printk("dev %d,%d ino %llu, mode 0x%x size %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, + __entry->mode, __entry->size) ); TRACE_EVENT(scoutfs_scan_orphans, diff --git a/kmod/src/xattr.c b/kmod/src/xattr.c index d2e57277..877626a4 100644 --- a/kmod/src/xattr.c +++ b/kmod/src/xattr.c @@ -554,46 +554,56 @@ out: } /* - * Delete all the xattr items associated with this inode. The caller - * holds a transaction. The inode is dead so we don't need the xattr - * rwsem. + * Delete all the xattr items associated with this inode. The inode is + * dead so we don't need the xattr rwsem. * * XXX This isn't great because it reads in all the items so that it can * create deletion items for each. It would be better to have the * caller create range deletion items for all the items covered by the * inode. That wouldn't require reading at all. */ -int scoutfs_xattr_drop(struct super_block *sb, u64 ino) +int scoutfs_xattr_drop(struct super_block *sb, u64 ino, + struct scoutfs_lock *lock) { struct scoutfs_key last; struct scoutfs_key key; - struct scoutfs_lock *lck; + unsigned int items = 16; + bool holding = false; int ret; init_xattr_key(&key, ino, 0, 0); init_xattr_key(&last, ino, U32_MAX, U64_MAX); - /* while we read to delete we need to writeback others */ - ret = scoutfs_lock_ino(sb, DLM_LOCK_EX, 0, ino, &lck); - if (ret) - goto out; - for (;;) { - ret = scoutfs_item_next(sb, &key, &last, NULL, lck); + ret = scoutfs_item_next(sb, &key, &last, NULL, lock); if (ret < 0) { if (ret == -ENOENT) ret = 0; break; } - ret = scoutfs_item_delete(sb, &key, lck); + if (!holding) { + ret = scoutfs_hold_trans(sb, SIC_EXACT(items, 0)); + if (ret) + break; + holding = true; + } + + ret = scoutfs_item_delete(sb, &key, lock); if (ret) break; - key.skx_part++; + if (--items == 0) { + scoutfs_release_trans(sb); + holding = false; + items = 16; + } + + /* don't need to inc, next won't see deleted item */ } - scoutfs_unlock(sb, lck, DLM_LOCK_EX); -out: + if (holding) + scoutfs_release_trans(sb); + return ret; } diff --git a/kmod/src/xattr.h b/kmod/src/xattr.h index e0fadf32..6c205358 100644 --- a/kmod/src/xattr.h +++ b/kmod/src/xattr.h @@ -8,6 +8,7 @@ int scoutfs_setxattr(struct dentry *dentry, const char *name, int scoutfs_removexattr(struct dentry *dentry, const char *name); ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size); -int scoutfs_xattr_drop(struct super_block *sb, u64 ino); +int scoutfs_xattr_drop(struct super_block *sb, u64 ino, + struct scoutfs_lock *lock); #endif