diff options
| author | Ingo Molnar <mingo@kernel.org> | 2024-03-25 13:32:29 +0300 | 
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2024-03-25 13:32:29 +0300 | 
| commit | f4566a1e73957800df75a3dd2dccee8a4697f327 (patch) | |
| tree | b043b875228c0b25988af66c680d60cae69d761d /fs/xfs/xfs_inode.c | |
| parent | b9e6e28663928cab836a19abbdec3d036a07db3b (diff) | |
| parent | 4cece764965020c22cff7665b18a012006359095 (diff) | |
| download | linux-f4566a1e73957800df75a3dd2dccee8a4697f327.tar.xz | |
Merge tag 'v6.9-rc1' into sched/core, to pick up fixes and to refresh the branch
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'fs/xfs/xfs_inode.c')
| -rw-r--r-- | fs/xfs/xfs_inode.c | 274 | 
1 files changed, 216 insertions, 58 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 1fd94958aa97..ea48774f6b76 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -203,9 +203,9 @@ xfs_ilock(  	}  	if (lock_flags & XFS_ILOCK_EXCL) -		mrupdate_nested(&ip->i_lock, XFS_ILOCK_DEP(lock_flags)); +		down_write_nested(&ip->i_lock, XFS_ILOCK_DEP(lock_flags));  	else if (lock_flags & XFS_ILOCK_SHARED) -		mraccess_nested(&ip->i_lock, XFS_ILOCK_DEP(lock_flags)); +		down_read_nested(&ip->i_lock, XFS_ILOCK_DEP(lock_flags));  }  /* @@ -246,10 +246,10 @@ xfs_ilock_nowait(  	}  	if (lock_flags & XFS_ILOCK_EXCL) { -		if (!mrtryupdate(&ip->i_lock)) +		if (!down_write_trylock(&ip->i_lock))  			goto out_undo_mmaplock;  	} else if (lock_flags & XFS_ILOCK_SHARED) { -		if (!mrtryaccess(&ip->i_lock)) +		if (!down_read_trylock(&ip->i_lock))  			goto out_undo_mmaplock;  	}  	return 1; @@ -298,9 +298,9 @@ xfs_iunlock(  		up_read(&VFS_I(ip)->i_mapping->invalidate_lock);  	if (lock_flags & XFS_ILOCK_EXCL) -		mrunlock_excl(&ip->i_lock); +		up_write(&ip->i_lock);  	else if (lock_flags & XFS_ILOCK_SHARED) -		mrunlock_shared(&ip->i_lock); +		up_read(&ip->i_lock);  	trace_xfs_iunlock(ip, lock_flags, _RET_IP_);  } @@ -319,7 +319,7 @@ xfs_ilock_demote(  		~(XFS_IOLOCK_EXCL|XFS_MMAPLOCK_EXCL|XFS_ILOCK_EXCL)) == 0);  	if (lock_flags & XFS_ILOCK_EXCL) -		mrdemote(&ip->i_lock); +		downgrade_write(&ip->i_lock);  	if (lock_flags & XFS_MMAPLOCK_EXCL)  		downgrade_write(&VFS_I(ip)->i_mapping->invalidate_lock);  	if (lock_flags & XFS_IOLOCK_EXCL) @@ -328,52 +328,30 @@ xfs_ilock_demote(  	trace_xfs_ilock_demote(ip, lock_flags, _RET_IP_);  } -#if defined(DEBUG) || defined(XFS_WARN) -static inline bool -__xfs_rwsem_islocked( -	struct rw_semaphore	*rwsem, -	bool			shared) -{ -	if (!debug_locks) -		return rwsem_is_locked(rwsem); - -	if (!shared) -		return lockdep_is_held_type(rwsem, 0); - -	/* -	 * We are checking that the lock is held at least in shared -	 * mode but don't care that it might be held exclusively -	 * (i.e. shared | excl). Hence we check if the lock is held -	 * in any mode rather than an explicit shared mode. -	 */ -	return lockdep_is_held_type(rwsem, -1); -} - -bool -xfs_isilocked( +void +xfs_assert_ilocked(  	struct xfs_inode	*ip,  	uint			lock_flags)  { -	if (lock_flags & (XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)) { -		if (!(lock_flags & XFS_ILOCK_SHARED)) -			return !!ip->i_lock.mr_writer; -		return rwsem_is_locked(&ip->i_lock.mr_lock); -	} - -	if (lock_flags & (XFS_MMAPLOCK_EXCL|XFS_MMAPLOCK_SHARED)) { -		return __xfs_rwsem_islocked(&VFS_I(ip)->i_mapping->invalidate_lock, -				(lock_flags & XFS_MMAPLOCK_SHARED)); -	} +	/* +	 * Sometimes we assert the ILOCK is held exclusively, but we're in +	 * a workqueue, so lockdep doesn't know we're the owner. +	 */ +	if (lock_flags & XFS_ILOCK_SHARED) +		rwsem_assert_held(&ip->i_lock); +	else if (lock_flags & XFS_ILOCK_EXCL) +		rwsem_assert_held_write_nolockdep(&ip->i_lock); -	if (lock_flags & (XFS_IOLOCK_EXCL | XFS_IOLOCK_SHARED)) { -		return __xfs_rwsem_islocked(&VFS_I(ip)->i_rwsem, -				(lock_flags & XFS_IOLOCK_SHARED)); -	} +	if (lock_flags & XFS_MMAPLOCK_SHARED) +		rwsem_assert_held(&VFS_I(ip)->i_mapping->invalidate_lock); +	else if (lock_flags & XFS_MMAPLOCK_EXCL) +		rwsem_assert_held_write(&VFS_I(ip)->i_mapping->invalidate_lock); -	ASSERT(0); -	return false; +	if (lock_flags & XFS_IOLOCK_SHARED) +		rwsem_assert_held(&VFS_I(ip)->i_rwsem); +	else if (lock_flags & XFS_IOLOCK_EXCL) +		rwsem_assert_held_write(&VFS_I(ip)->i_rwsem);  } -#endif  /*   * xfs_lockdep_subclass_ok() is only used in an ASSERT, so is only called when @@ -671,7 +649,7 @@ xfs_lookup(  out_free_name:  	if (ci_name) -		kmem_free(ci_name->name); +		kfree(ci_name->name);  out_unlock:  	*ipp = NULL;  	return error; @@ -802,6 +780,8 @@ xfs_init_new_inode(  	 */  	if ((pip && ino == pip->i_ino) || !xfs_verify_dir_ino(mp, ino)) {  		xfs_alert(mp, "Allocated a known in-use inode 0x%llx!", ino); +		xfs_agno_mark_sick(mp, XFS_INO_TO_AGNO(mp, ino), +				XFS_SICK_AG_INOBT);  		return -EFSCORRUPTED;  	} @@ -947,6 +927,81 @@ xfs_bumplink(  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);  } +#ifdef CONFIG_XFS_LIVE_HOOKS +/* + * Use a static key here to reduce the overhead of directory live update hooks. + * If the compiler supports jump labels, the static branch will be replaced by + * a nop sled when there are no hook users.  Online fsck is currently the only + * caller, so this is a reasonable tradeoff. + * + * Note: Patching the kernel code requires taking the cpu hotplug lock.  Other + * parts of the kernel allocate memory with that lock held, which means that + * XFS callers cannot hold any locks that might be used by memory reclaim or + * writeback when calling the static_branch_{inc,dec} functions. + */ +DEFINE_STATIC_XFS_HOOK_SWITCH(xfs_dir_hooks_switch); + +void +xfs_dir_hook_disable(void) +{ +	xfs_hooks_switch_off(&xfs_dir_hooks_switch); +} + +void +xfs_dir_hook_enable(void) +{ +	xfs_hooks_switch_on(&xfs_dir_hooks_switch); +} + +/* Call hooks for a directory update relating to a child dirent update. */ +inline void +xfs_dir_update_hook( +	struct xfs_inode		*dp, +	struct xfs_inode		*ip, +	int				delta, +	const struct xfs_name		*name) +{ +	if (xfs_hooks_switched_on(&xfs_dir_hooks_switch)) { +		struct xfs_dir_update_params	p = { +			.dp		= dp, +			.ip		= ip, +			.delta		= delta, +			.name		= name, +		}; +		struct xfs_mount	*mp = ip->i_mount; + +		xfs_hooks_call(&mp->m_dir_update_hooks, 0, &p); +	} +} + +/* Call the specified function during a directory update. */ +int +xfs_dir_hook_add( +	struct xfs_mount	*mp, +	struct xfs_dir_hook	*hook) +{ +	return xfs_hooks_add(&mp->m_dir_update_hooks, &hook->dirent_hook); +} + +/* Stop calling the specified function during a directory update. */ +void +xfs_dir_hook_del( +	struct xfs_mount	*mp, +	struct xfs_dir_hook	*hook) +{ +	xfs_hooks_del(&mp->m_dir_update_hooks, &hook->dirent_hook); +} + +/* Configure directory update hook functions. */ +void +xfs_dir_hook_setup( +	struct xfs_dir_hook	*hook, +	notifier_fn_t		mod_fn) +{ +	xfs_hook_setup(&hook->dirent_hook, mod_fn); +} +#endif /* CONFIG_XFS_LIVE_HOOKS */ +  int  xfs_create(  	struct mnt_idmap	*idmap, @@ -1058,6 +1113,12 @@ xfs_create(  	}  	/* +	 * Create ip with a reference from dp, and add '.' and '..' references +	 * if it's a directory. +	 */ +	xfs_dir_update_hook(dp, ip, 1, name); + +	/*  	 * If this is a synchronous mount, make sure that the  	 * create transaction goes to disk before returning to  	 * the user. @@ -1271,6 +1332,7 @@ xfs_link(  	xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE);  	xfs_bumplink(tp, sip); +	xfs_dir_update_hook(tdp, sip, 1, target_name);  	/*  	 * If this is a synchronous mount, make sure that the @@ -1342,9 +1404,9 @@ xfs_itruncate_extents_flags(  	xfs_fileoff_t		first_unmap_block;  	int			error = 0; -	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); -	ASSERT(!atomic_read(&VFS_I(ip)->i_count) || -	       xfs_isilocked(ip, XFS_IOLOCK_EXCL)); +	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL); +	if (atomic_read(&VFS_I(ip)->i_count)) +		xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);  	ASSERT(new_size <= XFS_ISIZE(ip));  	ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);  	ASSERT(ip->i_itemp != NULL); @@ -1596,7 +1658,7 @@ xfs_inactive_ifree(  	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);  	error = xfs_ifree(tp, ip); -	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); +	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);  	if (error) {  		/*  		 * If we fail to free the inode, shut down.  The cancel @@ -1677,6 +1739,39 @@ xfs_inode_needs_inactive(  }  /* + * Save health status somewhere, if we're dumping an inode with uncorrected + * errors and online repair isn't running. + */ +static inline void +xfs_inactive_health( +	struct xfs_inode	*ip) +{ +	struct xfs_mount	*mp = ip->i_mount; +	struct xfs_perag	*pag; +	unsigned int		sick; +	unsigned int		checked; + +	xfs_inode_measure_sickness(ip, &sick, &checked); +	if (!sick) +		return; + +	trace_xfs_inode_unfixed_corruption(ip, sick); + +	if (sick & XFS_SICK_INO_FORGET) +		return; + +	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); +	if (!pag) { +		/* There had better still be a perag structure! */ +		ASSERT(0); +		return; +	} + +	xfs_ag_mark_sick(pag, XFS_SICK_AG_INODES); +	xfs_perag_put(pag); +} + +/*   * xfs_inactive   *   * This is called when the vnode reference count for the vnode @@ -1704,6 +1799,8 @@ xfs_inactive(  	mp = ip->i_mount;  	ASSERT(!xfs_iflags_test(ip, XFS_IRECOVERY)); +	xfs_inactive_health(ip); +  	/*  	 * If this is a read-only mount, don't do this (would generate I/O)  	 * unless we're in log recovery and cleaning the iunlinked list. @@ -1910,6 +2007,7 @@ xfs_iunlink_update_bucket(  	 */  	if (old_value == new_agino) {  		xfs_buf_mark_corrupt(agibp); +		xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);  		return -EFSCORRUPTED;  	} @@ -1959,11 +2057,14 @@ xfs_iunlink_reload_next(  	 */  	ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, next_agino);  	error = xfs_iget(mp, tp, ino, XFS_IGET_UNTRUSTED, 0, &next_ip); -	if (error) +	if (error) { +		xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);  		return error; +	}  	/* If this is not an unlinked inode, something is very wrong. */  	if (VFS_I(next_ip)->i_nlink != 0) { +		xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);  		error = -EFSCORRUPTED;  		goto rele;  	} @@ -2001,6 +2102,7 @@ xfs_iunlink_insert_inode(  	if (next_agino == agino ||  	    !xfs_verify_agino_or_null(pag, next_agino)) {  		xfs_buf_mark_corrupt(agibp); +		xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);  		return -EFSCORRUPTED;  	} @@ -2088,6 +2190,7 @@ xfs_iunlink_remove_inode(  	if (!xfs_verify_agino(pag, head_agino)) {  		XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,  				agi, sizeof(*agi)); +		xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);  		return -EFSCORRUPTED;  	} @@ -2116,8 +2219,10 @@ xfs_iunlink_remove_inode(  		struct xfs_inode	*prev_ip;  		prev_ip = xfs_iunlink_lookup(pag, ip->i_prev_unlinked); -		if (!prev_ip) +		if (!prev_ip) { +			xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE);  			return -EFSCORRUPTED; +		}  		error = xfs_iunlink_log_inode(tp, prev_ip, pag,  				ip->i_next_unlinked); @@ -2350,7 +2455,7 @@ xfs_ifree(  	struct xfs_inode_log_item *iip = ip->i_itemp;  	int			error; -	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); +	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);  	ASSERT(VFS_I(ip)->i_nlink == 0);  	ASSERT(ip->i_df.if_nextents == 0);  	ASSERT(ip->i_disk_size == 0 || !S_ISREG(VFS_I(ip)->i_mode)); @@ -2378,7 +2483,7 @@ xfs_ifree(  	 * already been freed by xfs_attr_inactive.  	 */  	if (ip->i_df.if_format == XFS_DINODE_FMT_LOCAL) { -		kmem_free(ip->i_df.if_data); +		kfree(ip->i_df.if_data);  		ip->i_df.if_data = NULL;  		ip->i_df.if_bytes = 0;  	} @@ -2419,7 +2524,7 @@ static void  xfs_iunpin(  	struct xfs_inode	*ip)  { -	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); +	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED);  	trace_xfs_inode_unpin_nowait(ip, _RET_IP_); @@ -2585,6 +2690,12 @@ xfs_remove(  	}  	/* +	 * Drop the link from dp to ip, and if ip was a directory, remove the +	 * '.' and '..' references since we freed the directory. +	 */ +	xfs_dir_update_hook(dp, ip, -1, name); + +	/*  	 * If this is a synchronous mount, make sure that the  	 * remove transaction goes to disk before returning to  	 * the user. @@ -2774,6 +2885,20 @@ xfs_cross_rename(  	}  	xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);  	xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE); + +	/* +	 * Inform our hook clients that we've finished an exchange operation as +	 * follows: removed the source and target files from their directories; +	 * added the target to the source directory; and added the source to +	 * the target directory.  All inodes are locked, so it's ok to model a +	 * rename this way so long as we say we deleted entries before we add +	 * new ones. +	 */ +	xfs_dir_update_hook(dp1, ip1, -1, name1); +	xfs_dir_update_hook(dp2, ip2, -1, name2); +	xfs_dir_update_hook(dp1, ip2, 1, name1); +	xfs_dir_update_hook(dp2, ip1, 1, name2); +  	return xfs_finish_rename(tp);  out_trans_abort: @@ -3157,6 +3282,21 @@ retry:  	if (new_parent)  		xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); +	/* +	 * Inform our hook clients that we've finished a rename operation as +	 * follows: removed the source and target files from their directories; +	 * that we've added the source to the target directory; and finally +	 * that we've added the whiteout, if there was one.  All inodes are +	 * locked, so it's ok to model a rename this way so long as we say we +	 * deleted entries before we add new ones. +	 */ +	if (target_ip) +		xfs_dir_update_hook(target_dp, target_ip, -1, target_name); +	xfs_dir_update_hook(src_dp, src_ip, -1, src_name); +	xfs_dir_update_hook(target_dp, src_ip, 1, target_name); +	if (wip) +		xfs_dir_update_hook(src_dp, wip, 1, src_name); +  	error = xfs_finish_rename(tp);  	if (wip)  		xfs_irele(wip); @@ -3182,7 +3322,7 @@ xfs_iflush(  	struct xfs_mount	*mp = ip->i_mount;  	int			error; -	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); +	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED);  	ASSERT(xfs_iflags_test(ip, XFS_IFLUSHING));  	ASSERT(ip->i_df.if_format != XFS_DINODE_FMT_BTREE ||  	       ip->i_df.if_nextents > XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)); @@ -3317,6 +3457,8 @@ flush_out:  	/* generate the checksum. */  	xfs_dinode_calc_crc(mp, dip); +	if (error) +		xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE);  	return error;  } @@ -3777,3 +3919,19 @@ xfs_ifork_zapped(  		return false;  	}  } + +/* Compute the number of data and realtime blocks used by a file. */ +void +xfs_inode_count_blocks( +	struct xfs_trans	*tp, +	struct xfs_inode	*ip, +	xfs_filblks_t		*dblocks, +	xfs_filblks_t		*rblocks) +{ +	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); + +	*rblocks = 0; +	if (XFS_IS_REALTIME_INODE(ip)) +		xfs_bmap_count_leaves(ifp, rblocks); +	*dblocks = ip->i_nblocks - *rblocks; +}  | 
