Merge pull request #341 from versity/zab/extent_mod_lock_hold_times

Zab/extent mod lock hold times
This commit is contained in:
Zach Brown
2026-10-14 19:12:06 -07:00
committed by GitHub
9 changed files with 307 additions and 143 deletions
+48 -19
View File
@@ -195,7 +195,7 @@ struct scoutfs_ext_ops data_ext_ops = {
* - +iblock of next logical block to truncate the next block from
*/
static s64 truncate_extents(struct super_block *sb, struct inode *inode,
u64 ino, u64 iblock, u64 last, bool offline,
u64 ino, u64 iblock, u64 last, int dti,
struct scoutfs_lock *lock)
{
DECLARE_DATA_INFO(sb, datinf);
@@ -212,7 +212,7 @@ static s64 truncate_extents(struct super_block *sb, struct inode *inode,
int err;
int i;
flags = offline ? SEF_OFFLINE : 0;
flags = (dti & DTI_OFFLINE) ? SEF_OFFLINE : 0;
ret = 0;
for (i = 0; iblock <= last; i++) {
@@ -236,7 +236,7 @@ static s64 truncate_extents(struct super_block *sb, struct inode *inode,
}
/* nothing to do when already offline and unmapped */
if ((offline && (ext.flags & SEF_OFFLINE)) && !ext.map) {
if (((dti & DTI_OFFLINE) && (ext.flags & SEF_OFFLINE)) && !ext.map) {
iblock = ext.start + ext.len;
continue;
}
@@ -288,26 +288,32 @@ static s64 truncate_extents(struct super_block *sb, struct inode *inode,
* Free blocks inside the logical block range from 'iblock' to 'last',
* inclusive.
*
* If 'offline' is given then blocks are freed an offline mapping is
* left behind. Only blocks that have been allocated can be marked
* offline.
* The DTI_ flags change behavior as described at their definition.
*
* If the inode is provided then we update its tracking of the online
* and offline blocks. If it's not provided then the inode is being
* destroyed and isn't reachable, we don't need to update it.
* If the inode is provided then we update its item and the tracking of
* online and offline blocks. If it's not provided then the inode is
* being destroyed and isn't reachable, we don't need to update it.
*
* The caller is in charge of locking the inode and data, but we may
* have to modify far more items than fit in a transaction so we're in
* charge of batching updates into transactions. If the inode is
* provided then we're responsible for updating its item as we go.
* The caller is in charge of locking the inode and data while we manage
* transactions. dti flags can have us return partial progress so the
* caller can drop locks.
*/
int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
u64 ino, u64 iblock, u64 last, bool offline,
s64 scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
u64 ino, u64 iblock, u64 last, int dti,
struct scoutfs_lock *lock)
{
struct scoutfs_inode_info *si = NULL;
LIST_HEAD(ind_locks);
u64 after_isize;
u64 offline;
u64 online;
s64 ret = 0;
u64 seq;
if (WARN_ON_ONCE((dti & (DTI_START_ISIZE|DTI_ONLINE_DONE)) && !inode)) {
ret = -EINVAL;
goto out;
}
WARN_ON_ONCE(inode && !inode_is_locked(inode));
@@ -315,7 +321,22 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
if (last > SCOUTFS_BLOCK_SM_MAX)
last = SCOUTFS_BLOCK_SM_MAX;
trace_scoutfs_data_truncate_items(sb, iblock, last, offline);
if (dti & DTI_START_ISIZE) {
after_isize = (i_size_read(inode) + SCOUTFS_BLOCK_SM_SIZE - 1)
>> SCOUTFS_BLOCK_SM_SHIFT;
if (iblock < after_isize)
iblock = min(after_isize, last);
}
if (dti & DTI_ONLINE_DONE) {
scoutfs_inode_get_onoff(inode, &online, &offline);
if (online > 0) {
ret = 0;
goto out;
}
}
trace_scoutfs_data_truncate_items(sb, iblock, last, dti);
if (WARN_ON_ONCE(last < iblock))
return -EINVAL;
@@ -323,7 +344,16 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
if (inode)
si = SCOUTFS_I(inode);
if (dti & DTI_PARTIAL_INVALIDATE)
seq = scoutfs_trans_sample_seq(sb);
while (iblock <= last) {
if ((dti & DTI_PARTIAL_INVALIDATE) && iblock > 0 &&
scoutfs_lock_invalidate_pending(lock) && scoutfs_trans_sample_seq(sb) != seq) {
ret = iblock;
break;
}
if (inode)
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, true, false);
else
@@ -339,8 +369,7 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
}
if (ret == 0)
ret = truncate_extents(sb, inode, ino, iblock, last,
offline, lock);
ret = truncate_extents(sb, inode, ino, iblock, last, dti, lock);
if (inode)
scoutfs_update_inode_item(inode, lock, &ind_locks);
@@ -356,7 +385,7 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
iblock = ret;
ret = 0;
}
out:
return ret;
}
+26 -2
View File
@@ -66,8 +66,32 @@ int scoutfs_data_alloc_block(struct super_block *sb, struct inode *inode,
int scoutfs_get_block_write(struct inode *inode, sector_t iblock, struct buffer_head *bh,
int create);
int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
u64 ino, u64 iblock, u64 last, bool offline,
enum {
/*
* Set offline flag in extents when blocks are freed.
*/
DTI_OFFLINE = (1 << 0),
/*
* Set the starting block to the block after isize, only if the
* initial starting block is less. Requires a valid inode
* pointer.
*/
DTI_START_ISIZE = (1 << 1),
/*
* Stop and return 0 if there are any online blocks. Requires
* a valid inode pointer.
*/
DTI_ONLINE_DONE = (1 << 2),
/*
* Allow partial progress when there's an invalidation pending
* on the inode lock. Will always make at least a transaction's
* worth of progress truncating. If partial progress was made
* then the next block to resume truncating from is returned.
*/
DTI_PARTIAL_INVALIDATE = (1 << 3),
};
s64 scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
u64 ino, u64 iblock, u64 last, int dti,
struct scoutfs_lock *lock);
int scoutfs_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 len);
+53 -25
View File
@@ -460,8 +460,7 @@ int scoutfs_complete_truncate(struct inode *inode, struct scoutfs_lock *lock)
start = (i_size_read(inode) + SCOUTFS_BLOCK_SM_SIZE - 1) >>
SCOUTFS_BLOCK_SM_SHIFT;
ret = scoutfs_data_truncate_items(inode->i_sb, inode,
scoutfs_ino(inode), start, ~0ULL,
false, lock);
scoutfs_ino(inode), start, U64_MAX, 0, lock);
err = clear_truncate_flag(inode, lock);
return ret ? ret : err;
@@ -1635,16 +1634,20 @@ int scoutfs_inode_orphan_delete(struct super_block *sb, u64 ino, struct scoutfs_
}
/*
* Remove all the items associated with a given inode. This is only
* called once nlink has dropped to zero and nothing has the inode open
* so we don't have to worry about dirents referencing the inode or link
* Remove the items associated with a given inode. This is only called
* once nlink has dropped to zero and nothing has the inode open so we
* don't have to worry about dirents referencing the inode or link
* backrefs. Dropping nlink to 0 also created an orphan item. That
* orphan item will continue triggering attempts to finish previous
* partial deletion until all deletion is complete and the orphan item
* is removed.
*
* Returns a positive block number if data extent removal made partial
* progress and could be continued from that block number.
*/
static int delete_inode_items(struct super_block *sb, u64 ino, struct scoutfs_inode *sinode,
struct scoutfs_lock *lock, struct scoutfs_lock *orph_lock)
static s64 delete_inode_items(struct super_block *sb, u64 ino, u64 sblock,
struct scoutfs_inode *sinode, struct scoutfs_lock *lock,
struct scoutfs_lock *orph_lock)
{
struct scoutfs_key key;
LIST_HEAD(ind_locks);
@@ -1652,7 +1655,7 @@ static int delete_inode_items(struct super_block *sb, u64 ino, struct scoutfs_in
umode_t mode;
u64 ind_seq;
u64 size;
int ret;
s64 ret;
scoutfs_inode_init_key(&key, ino);
@@ -1662,9 +1665,9 @@ static int delete_inode_items(struct super_block *sb, u64 ino, struct scoutfs_in
/* 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)
ret = scoutfs_data_truncate_items(sb, NULL, ino, sblock, U64_MAX,
DTI_PARTIAL_INVALIDATE, lock);
if (ret != 0)
goto out;
}
@@ -1787,7 +1790,7 @@ out:
}
/*
* Try to delete all the items for an unused inode number. This is the
* Try to delete the items for an unused inode number. This is the
* relatively slow path that uses cluster locks, network requests, and
* IO to ensure correctness. Callers should try hard to avoid calling
* when there's no work to do.
@@ -1811,10 +1814,14 @@ out:
* on a bit in the lock data so that we only have one deletion attempt
* per inode under this mount's cluster lock.
*
* Returns -EAGAIN if we either did some cleanup work or are unable to finish
* cleaning up this inode right now.
* Returns -EAGAIN if we either did some cleanup work or are unable to
* finish cleaning up this inode right now.
*
* This can make partial progress if it had to drop the inode lock while
* removing extent items. In this case it returns the postive starting
* data block number that truncate could resume from.
*/
static int try_delete_inode_items(struct super_block *sb, u64 ino)
static s64 try_delete_inode_items(struct super_block *sb, u64 ino, u64 sblock)
{
struct inode_deletion_lock_data *ldata = NULL;
struct scoutfs_lock *orph_lock = NULL;
@@ -1824,7 +1831,7 @@ static int try_delete_inode_items(struct super_block *sb, u64 ino)
bool clear_trying = false;
u64 group_nr;
int bit_nr;
int ret;
s64 ret;
trace_scoutfs_try_delete(sb, ino);
@@ -1873,7 +1880,7 @@ static int try_delete_inode_items(struct super_block *sb, u64 ino)
if (ret < 0)
goto out;
ret = delete_inode_items(sb, ino, &sinode, lock, orph_lock);
ret = delete_inode_items(sb, ino, sblock, &sinode, lock, orph_lock);
if (ret == 0) {
ret = -EAGAIN;
scoutfs_inc_counter(sb, inode_deleted);
@@ -1890,12 +1897,33 @@ out:
}
/*
* As we evicted an inode we need to decide to try and delete its items
* or not, which is expensive. We only try when we have lock coverage
* and the inode has been unlinked. This catches the common case of
* regular deletion so deletion will be performed in the final unlink
* task. It also catches open-unlink or o_tmpfile that aren't cached on
* other nodes.
* Keep deleting the inode's items as long as we make partial progress
* deleting extent items.
*
* Returns -EAGAIN if we either did some cleanup work or are unable to
* finish cleaning up this inode right now.
*/
static int try_delete_inode(struct super_block *sb, u64 ino)
{
u64 sblock = 0;
s64 ret;
do {
ret = try_delete_inode_items(sb, ino, sblock);
if (ret > 0)
sblock = ret;
} while (ret > 0);
return ret;
}
/*
* As we evict an inode we decide if we should delete its items or not,
* which is expensive. We only try when we have lock coverage and the
* inode has been unlinked. This catches the common case of regular
* deletion so deletion will be performed in the final unlink task. It
* also catches open-unlink or o_tmpfile that aren't cached on other
* nodes.
*
* Inodes being evicted outside of lock coverage, by referenced dentries
* or inodes that survived the attempt to drop them as their lock was
@@ -1918,7 +1946,7 @@ void scoutfs_evict_inode(struct inode *inode)
scoutfs_omap_clear(sb, ino);
if (scoutfs_lock_is_covered(sb, &si->ino_lock_cov) && inode->i_nlink == 0)
try_delete_inode_items(sb, scoutfs_ino(inode));
try_delete_inode(sb, scoutfs_ino(inode));
}
clear_inode(inode);
@@ -2164,7 +2192,7 @@ static void inode_orphan_scan_worker(struct work_struct *work)
scoutfs_inc_counter(sb, orphan_scan_attempts);
trace_scoutfs_orphan_scan_work(sb, ino);
ret = try_delete_inode_items(sb, ino);
ret = try_delete_inode(sb, ino);
if (ret == -EAGAIN) {
work_todo = true;
ret = 0;
+70 -55
View File
@@ -298,6 +298,61 @@ out:
return ret;
}
static int release_range(struct super_block *sb, struct inode *inode, u64 data_version,
u64 sblock, u64 eblock, int dti)
{
struct scoutfs_lock *lock = NULL;
bool unlock = false;
int ret;
while (sblock <= eblock) {
inode_lock(inode);
unlock = true;
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE,
SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
if (ret < 0)
goto out;
if (!S_ISREG(inode->i_mode)) {
ret = -EINVAL;
goto out;
}
if (scoutfs_inode_data_version(inode) != data_version) {
ret = -ESTALE;
goto out;
}
inode_dio_wait(inode);
/* drop all clean and dirty cached blocks in the range */
truncate_inode_pages_range(&inode->i_data,
sblock << SCOUTFS_BLOCK_SM_SHIFT,
((eblock + 1) << SCOUTFS_BLOCK_SM_SHIFT) - 1);
ret = scoutfs_data_truncate_items(sb, inode, scoutfs_ino(inode), sblock, eblock,
dti | DTI_PARTIAL_INVALIDATE, lock);
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
lock = NULL;
inode_unlock(inode);
unlock = false;
if (ret <= 0)
goto out;
sblock = ret;
}
ret = 0;
out:
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
if (unlock)
inode_unlock(inode);
return ret;
}
/*
* The caller has a version of the data available in the given byte
* range in an external archive. As long as the data version still
@@ -306,23 +361,23 @@ out:
* recall from the archive.
*
* If the file's online blocks drop to 0 then we also truncate any
* blocks beyond i_size. This honors the intent of fully releasing a file
* without the user needing to know to release past i_size or truncate.
* blocks beyond i_size. This honors the intent of fully releasing a
* file without the user needing to know to release past i_size or
* truncate.
*
* XXX permissions?
* XXX a lot of this could be generic file write prep
* The inode's cluster lock can be unlocked multiple times while the
* region is released. This allows concurrent release operations to
* interleave. Normal data plane operations that would change
* data_version will block while there are offline blocks so they likely
* won't be concurrent with release.
*/
static long scoutfs_ioc_release(struct file *file, unsigned long arg)
{
struct inode *inode = file_inode(file);
struct super_block *sb = inode->i_sb;
struct scoutfs_ioctl_release args;
struct scoutfs_lock *lock = NULL;
u64 sblock;
u64 eblock;
u64 online;
u64 offline;
u64 isize;
__u64 tmp;
int ret;
@@ -338,60 +393,20 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg)
(args.length & SCOUTFS_BLOCK_SM_MASK))
return -EINVAL;
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
ret = mnt_want_write_file(file);
if (ret)
return ret;
inode_lock(inode);
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE,
SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
if (ret)
goto out;
if (!S_ISREG(inode->i_mode)) {
ret = -EINVAL;
goto out;
}
if (!(file->f_mode & FMODE_WRITE)) {
ret = -EINVAL;
goto out;
}
if (scoutfs_inode_data_version(inode) != args.data_version) {
ret = -ESTALE;
goto out;
}
inode_dio_wait(inode);
/* drop all clean and dirty cached blocks in the range */
truncate_inode_pages_range(&inode->i_data, args.offset,
args.offset + args.length - 1);
sblock = args.offset >> SCOUTFS_BLOCK_SM_SHIFT;
eblock = (args.offset + args.length - 1) >> SCOUTFS_BLOCK_SM_SHIFT;
ret = scoutfs_data_truncate_items(sb, inode, scoutfs_ino(inode),
sblock,
eblock, true,
lock);
if (ret == 0) {
scoutfs_inode_get_onoff(inode, &online, &offline);
isize = i_size_read(inode);
if (online == 0 && isize) {
sblock = (isize + SCOUTFS_BLOCK_SM_SIZE - 1)
>> SCOUTFS_BLOCK_SM_SHIFT;
ret = scoutfs_data_truncate_items(sb, inode,
scoutfs_ino(inode),
sblock, U64_MAX,
false, lock);
}
}
ret = release_range(sb, inode, args.data_version, sblock, eblock, DTI_OFFLINE);
if (ret == 0)
ret = release_range(sb, inode, args.data_version, 0, U64_MAX,
DTI_START_ISIZE | DTI_ONLINE_DONE);
out:
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
inode_unlock(inode);
mnt_drop_write_file(file);
trace_scoutfs_ioc_release_ret(sb, scoutfs_ino(inode), ret);
+89 -31
View File
@@ -78,7 +78,6 @@ struct lock_info {
struct super_block *sb;
spinlock_t lock;
bool shutdown;
bool unmounting;
struct rb_root lock_tree;
struct rb_root lock_range_tree;
u64 nr_locks;
@@ -321,21 +320,29 @@ static bool lock_counts_match(int granted, unsigned int *counts)
return true;
}
static bool lock_counts_zero(unsigned int *counts)
{
enum scoutfs_lock_mode mode;
for (mode = 0; mode < SCOUTFS_LOCK_NR_MODES; mode++) {
if (counts[mode])
return false;
}
return true;
}
/*
* An idle lock has nothing going on. It can be present in the lru and
* can be freed by the final put when it has a null mode.
*/
static bool lock_idle(struct scoutfs_lock *lock)
{
enum scoutfs_lock_mode mode;
if (lock->request_pending || lock->invalidate_pending)
return false;
for (mode = 0; mode < SCOUTFS_LOCK_NR_MODES; mode++) {
if (lock->waiters[mode] || lock->users[mode])
return false;
}
if (!lock_counts_zero(lock->waiters) || !lock_counts_zero(lock->users))
return false;
return true;
}
@@ -759,6 +766,26 @@ static void lock_invalidate_worker(struct work_struct *work)
spin_unlock(&linfo->lock);
}
/*
* Returns true if the requested mode doesn't match the next mode that
* would result after the next invalidation is processed.
*
* We have to be careful not to cause deadlocks by having a lock attempt
* wait for invalidation while the invalidation is waiting for the lock
* attempt to unlock any previous locks it might have. This can get
* gnarly when there are multiple invalidations queued and requests
* pending for elevated modes. We avoid all that and never let
* invalidations block lock attempts when there are any other users of
* the lock.
*/
static bool next_invalidate_blocks(struct scoutfs_lock *lock, u8 requested)
{
struct inv_req *ireq = list_first_entry_or_null(&lock->inv_list, struct inv_req, head);
return ireq && lock_counts_zero(lock->users) &&
!lock_modes_match(ireq->nl.new_mode, requested);
}
/*
* Add an incoming invalidation request to the end of the list on the
* lock and queue it for blocking invalidation work. This is being
@@ -950,7 +977,8 @@ static bool lock_wait_cond(struct super_block *sb, struct scoutfs_lock *lock,
bool wake;
spin_lock(&linfo->lock);
wake = linfo->shutdown || lock_modes_match(lock->mode, mode) ||
wake = scoutfs_forcing_unmount(sb) || linfo->shutdown ||
(lock_modes_match(lock->mode, mode) && !next_invalidate_blocks(lock, mode)) ||
!lock->request_pending;
spin_unlock(&linfo->lock);
@@ -1020,13 +1048,23 @@ static int lock_key_range(struct super_block *sb, enum scoutfs_lock_mode mode, i
lock_inc_count(lock->waiters, mode);
for (;;) {
/*
* Lock grant request error response handling is
* non-functional. We directly return when forcing
* unmount rather than via errors network requests.
*/
if (scoutfs_forcing_unmount(sb)) {
ret = -ENOLINK;
break;
}
if (WARN_ON_ONCE(linfo->shutdown)) {
ret = -ESHUTDOWN;
break;
}
/* the fast path where we can use the granted mode */
if (lock_modes_match(lock->mode, mode)) {
if (lock_modes_match(lock->mode, mode) && !next_invalidate_blocks(lock, mode)) {
lock_inc_count(lock->users, mode);
*ret_lock = lock;
ret = 0;
@@ -1040,7 +1078,7 @@ static int lock_key_range(struct super_block *sb, enum scoutfs_lock_mode mode, i
break;
}
if (!lock->request_pending) {
if (!lock_modes_match(lock->mode, mode) && !lock->request_pending) {
lock->request_pending = 1;
should_send = true;
} else {
@@ -1450,6 +1488,19 @@ bool scoutfs_lock_protected(struct scoutfs_lock *lock, struct scoutfs_key *key,
&lock->start, &lock->end) == 0;
}
/*
* Returns true if there is an invalidation pending on the lock.
*
* This is racey and doesn't test modes. But it can still be useful for
* writers who will take non-destructive action if there's an
* invalidation pending that must necessarily be incompatible with their
* hold.
*/
bool scoutfs_lock_invalidate_pending(struct scoutfs_lock *lock)
{
return lock->invalidate_pending;
}
void scoutfs_free_unused_locks(struct super_block *sb)
{
DECLARE_LOCK_INFO(sb, linfo);
@@ -1474,21 +1525,6 @@ static void lock_tseq_show(struct seq_file *m, struct scoutfs_tseq_entry *ent)
lock->users[SCOUTFS_LOCK_WRITE_ONLY]);
}
/*
* shrink_dcache_for_umount() tears down dentries with no locking. We
* need to make sure that our invalidation won't touch dentries before
* we return and the caller calls the generic vfs unmount path.
*/
void scoutfs_lock_unmount_begin(struct super_block *sb)
{
DECLARE_LOCK_INFO(sb, linfo);
if (linfo) {
linfo->unmounting = true;
flush_work(&linfo->inv_work);
}
}
void scoutfs_lock_flush_invalidate(struct super_block *sb)
{
DECLARE_LOCK_INFO(sb, linfo);
@@ -1497,6 +1533,32 @@ void scoutfs_lock_flush_invalidate(struct super_block *sb)
flush_work(&linfo->inv_work);
}
static void wake_all_locks(struct lock_info *linfo)
{
struct scoutfs_lock *lock;
struct rb_node *node;
spin_lock(&linfo->lock);
for (node = rb_first(&linfo->lock_tree); node; node = rb_next(node)) {
lock = rb_entry(node, struct scoutfs_lock, node);
wake_up(&lock->waitq);
}
spin_unlock(&linfo->lock);
}
/*
* Used to break waiting lock attempts that should fail during forced
* unmount but which aren't connected to error responses from the
* canceled net requests.
*/
void scoutfs_lock_kick_waiters(struct super_block *sb)
{
DECLARE_LOCK_INFO(sb, linfo);
if (linfo)
wake_all_locks(linfo);
}
static u64 get_held_lock_refresh_gen(struct super_block *sb, struct scoutfs_key *start)
{
DECLARE_LOCK_INFO(sb, linfo);
@@ -1553,8 +1615,6 @@ u64 scoutfs_lock_ino_refresh_gen(struct super_block *sb, u64 ino)
void scoutfs_lock_shutdown(struct super_block *sb)
{
DECLARE_LOCK_INFO(sb, linfo);
struct scoutfs_lock *lock;
struct rb_node *node;
if (!linfo)
return;
@@ -1564,11 +1624,9 @@ void scoutfs_lock_shutdown(struct super_block *sb)
/* cause current and future lock calls to return errors */
spin_lock(&linfo->lock);
linfo->shutdown = true;
for (node = rb_first(&linfo->lock_tree); node; node = rb_next(node)) {
lock = rb_entry(node, struct scoutfs_lock, node);
wake_up(&lock->waitq);
}
spin_unlock(&linfo->lock);
wake_all_locks(linfo);
}
/*
+2 -1
View File
@@ -103,14 +103,15 @@ void scoutfs_lock_del_coverage(struct super_block *sb,
struct scoutfs_lock_coverage *cov);
bool scoutfs_lock_protected(struct scoutfs_lock *lock, struct scoutfs_key *key,
enum scoutfs_lock_mode mode);
bool scoutfs_lock_invalidate_pending(struct scoutfs_lock *lock);
u64 scoutfs_lock_ino_refresh_gen(struct super_block *sb, u64 ino);
void scoutfs_free_unused_locks(struct super_block *sb);
int scoutfs_lock_setup(struct super_block *sb);
void scoutfs_lock_unmount_begin(struct super_block *sb);
void scoutfs_lock_flush_invalidate(struct super_block *sb);
void scoutfs_lock_kick_waiters(struct super_block *sb);
void scoutfs_lock_shutdown(struct super_block *sb);
void scoutfs_lock_destroy(struct super_block *sb);
+9 -9
View File
@@ -378,26 +378,26 @@ DEFINE_EVENT(scoutfs_data_file_extent_class, scoutfs_data_fiemap_extent,
);
TRACE_EVENT(scoutfs_data_truncate_items,
TP_PROTO(struct super_block *sb, __u64 iblock, __u64 last, int offline),
TP_PROTO(struct super_block *sb, __u64 iblock, __u64 last, int dti),
TP_ARGS(sb, iblock, last, offline),
TP_ARGS(sb, iblock, last, dti),
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
__field(__u64, iblock)
__field(__u64, last)
__field(int, offline)
__field(int, dti)
),
TP_fast_assign(
SCSB_TRACE_ASSIGN(sb);
__entry->iblock = iblock;
__entry->last = last;
__entry->offline = offline;
__entry->dti = dti;
),
TP_printk(SCSBF" iblock %llu last %llu offline %u", SCSB_TRACE_ARGS,
__entry->iblock, __entry->last, __entry->offline)
TP_printk(SCSBF" iblock %llu last %llu dti %x", SCSB_TRACE_ARGS,
__entry->iblock, __entry->last, __entry->dti)
);
TRACE_EVENT(scoutfs_data_wait_check,
@@ -1211,7 +1211,7 @@ TRACE_EVENT(scoutfs_delete_inode,
);
TRACE_EVENT(scoutfs_delete_inode_end,
TP_PROTO(struct super_block *sb, u64 ino, umode_t mode, u64 size, int ret),
TP_PROTO(struct super_block *sb, u64 ino, umode_t mode, u64 size, s64 ret),
TP_ARGS(sb, ino, mode, size, ret),
@@ -1220,7 +1220,7 @@ TRACE_EVENT(scoutfs_delete_inode_end,
__field(__u64, ino)
__field(umode_t, mode)
__field(__u64, size)
__field(int, ret)
__field(__s64, ret)
),
TP_fast_assign(
@@ -1231,7 +1231,7 @@ TRACE_EVENT(scoutfs_delete_inode_end,
__entry->ret = ret;
),
TP_printk("dev %d,%d ino %llu, mode 0x%x size %llu, ret %d",
TP_printk("dev %d,%d ino %llu, mode 0x%x size %llu, ret %lld",
MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino,
__entry->mode, __entry->size, __entry->ret)
);
+2 -1
View File
@@ -249,6 +249,7 @@ static void scoutfs_umount_begin(struct super_block *sb)
sbi->forced_unmount = true;
scoutfs_client_net_shutdown(sb);
scoutfs_lock_kick_waiters(sb);
}
static const struct super_operations scoutfs_super_ops = {
@@ -668,7 +669,7 @@ static void scoutfs_kill_sb(struct super_block *sb)
if (SCOUTFS_HAS_SBI(sb)) {
scoutfs_options_stop(sb);
scoutfs_inode_orphan_stop(sb);
scoutfs_lock_unmount_begin(sb);
scoutfs_lock_flush_invalidate(sb);
}
kill_block_super(sb);
+8
View File
@@ -19,4 +19,12 @@ rm -f "$T_D0/move"
echo "== cleanup"
rm -f "$T_D0/alloc"
#
# If the inodes were picked up by the scanners then extent freeing by
# final deletion could continune long after this text exited. Make sure
# the scanners are idle before finish so later tests don't have
# surprising background work.
#
t_wait_for_no_orphans
t_pass