XFS: replace the mount inode list with radix tree traversals V4
cover XFS: replace the mount inode list with radix tree traversals V4
The list of all inodes on a mount is superfluous. We can traverse all inodes now by walking the per-AG inode radix trees without needing a separate list. This enables us to remove a bunch of complex list traversal code and remove another two pointers from the xfs_inode.
Also, by replacing the sync traversal with an ascending inode number traversal, we will issue better inode I/O patterns for writeback triggered by xfssyncd or unmount.
Before we make this change, move all the relevant sync code into it's own file in the linux-2.6/ directory. This aggregates VFS specific sync interfacing in the one file and will allow all the subsequent change history to be associated with this file so it is easy to find in future.
Version 4:
o revert xfs_syncsub -> xfs_sync change in xfs_quiesce_fs and rediff patch series
PATCH 1/6 XFS: move sync code to its own file
The sync code in XFS is spread around several files. While it used to make sense to have such a distribution, the code is about to be cleaned up and so centralising it in one spot as the first step makes sense.
--- fs/xfs/Makefile | 1 + fs/xfs/linux-2.6/xfs_super.c | 1 + fs/xfs/linux-2.6/xfs_sync.c | 605 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/linux-2.6/xfs_sync.h | 7 + fs/xfs/xfs_vfsops.c | 560 +-------------------------------------- fs/xfs/xfs_vfsops.h | 1 - 6 files changed, 615 insertions(+), 560 deletions(-) create mode 100644 fs/xfs/linux-2.6/xfs_sync.c create mode 100644 fs/xfs/linux-2.6/xfs_sync.h
fs/xfs/Makefile +1 -0
@@ -106,6 +106,7 @@ xfs-y += $(addprefix $(XFS_LINUX)/, \ xfs_iops.o \ xfs_lrw.o \ xfs_super.o \ + xfs_sync.o \ xfs_vnode.o \ xfs_xattr.o)
fs/xfs/linux-2.6/xfs_super.c +1 -0
@@ -58,6 +58,7 @@#include "xfs_extfree_item.h" #include "xfs_mru_cache.h" #include "xfs_inode_item.h" +#include "xfs_sync.h" #include <linux/namei.h> #include <linux/init.h>
fs/xfs/linux-2.6/xfs_sync.c new file +605 -0
605 lines not on this page · raw message
fs/xfs/linux-2.6/xfs_sync.h new file +7 -0
@@ -0,0 +1,7 @@ +#ifndef XFS_SYNC_H +#define XFS_SYNC_H 1 + +int xfs_sync (struct xfs_mount *mp,int flags); +int xfs_syncsub (struct xfs_mount *mp,int flags,int *bypassed); + +#endif
fs/xfs/xfs_vfsops.c +1 -559
569 lines not on this page · raw message
fs/xfs/xfs_vfsops.h +0 -1
@@ -8,7 +8,6 @@ struct kstatfs;struct xfs_mount ;struct xfs_mount_args ; -int xfs_sync (struct xfs_mount *mp,int flags);void xfs_do_force_shutdown (struct xfs_mount *mp,int flags,char *fname,int lnnum);void xfs_attr_quiesce (struct xfs_mount *mp);
Signature · 1 line
1.5.6
PATCH 2/6 XFS: move xfssyncd code to xfs_sync.c
In reply to Dave ChinnerMove all the xfssyncd code to the new xfs_sync.c file. This places it closer to the actual code that it interacts with, rather than just being associated with high level VFS code.
--- fs/xfs/linux-2.6/xfs_super.c | 151 +-------------------------------------- fs/xfs/linux-2.6/xfs_super.h | 3 - fs/xfs/linux-2.6/xfs_sync.c | 163 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/linux-2.6/xfs_sync.h | 56 ++++++++++++++ fs/xfs/linux-2.6/xfs_vfs.h | 31 -------- fs/xfs/xfs_mount.h | 1 + 6 files changed, 223 insertions(+), 182 deletions(-)
fs/xfs/linux-2.6/xfs_super.c +3 -148
@@ -944,146 +944,6 @@ xfs_fs_clear_inode(ASSERT (XFS_I (inode) ==NULL ); } -/* -* Enqueue a work item to be picked up by the vfs xfssyncd thread. -* Doing this has two advantages: -* - It saves on stack space, which is tight in certain situations -* - It can be used (with care) as a mechanism to avoid deadlocks. -* Flushing while allocating in a full filesystem requires both. -*/ -STATIC void -xfs_syncd_queue_work ( -struct xfs_mount *mp, -void *data, -void (*syncer)(struct xfs_mount *,void *)) -{ -struct bhv_vfs_sync_work *work; - - work =kmem_alloc (sizeof (struct bhv_vfs_sync_work ),KM_SLEEP ); -INIT_LIST_HEAD (&work->w_list); - work->w_syncer = syncer; - work->w_data = data; - work->w_mount = mp; -spin_lock (&mp->m_sync_lock); -list_add_tail (&work->w_list, &mp->m_sync_list); -spin_unlock (&mp->m_sync_lock); -wake_up_process (mp->m_sync_task); -} - -/* -* Flush delayed allocate data, attempting to free up reserved space -* from existing allocations. At this point a new allocation attempt -* has failed with ENOSPC and we are in the process of scratching our -* heads, looking about for more room... -*/ -STATIC void -xfs_flush_inode_work ( -struct xfs_mount *mp, -void *arg) -{ -struct inode *inode = arg; -filemap_flush (inode->i_mapping); -iput (inode); -} - -void -xfs_flush_inode ( -xfs_inode_t *ip) -{ -struct inode *inode =VFS_I (ip); - -igrab (inode); -xfs_syncd_queue_work (ip->i_mount, inode, xfs_flush_inode_work); -delay (msecs_to_jiffies (500 )); -} - -/* -* This is the "bigger hammer" version of xfs_flush_inode_work... -* (IOW, "If at first you don't succeed, use a Bigger Hammer"). -*/ -STATIC void -xfs_flush_device_work ( -struct xfs_mount *mp, -void *arg) -{ -struct inode *inode = arg; -sync_blockdev (mp->m_super->s_bdev); -iput (inode); -} - -void -xfs_flush_device ( -xfs_inode_t *ip) -{ -struct inode *inode =VFS_I (ip); - -igrab (inode); -xfs_syncd_queue_work (ip->i_mount, inode, xfs_flush_device_work); -delay (msecs_to_jiffies (500 )); -xfs_log_force (ip->i_mount, (xfs_lsn_t )0 ,XFS_LOG_FORCE |XFS_LOG_SYNC ); -} - -STATIC void -xfs_sync_worker ( -struct xfs_mount *mp, -void *unused) -{ -int error; - -if (!(mp->m_flags &XFS_MOUNT_RDONLY )) - error =xfs_sync (mp,SYNC_FSDATA |SYNC_BDFLUSH |SYNC_ATTR ); - mp->m_sync_seq++; -wake_up (&mp->m_wait_single_sync_task); -} - -STATIC int -xfssyncd ( -void *arg) -{ -struct xfs_mount *mp = arg; -long timeleft; -bhv_vfs_sync_work_t *work, *n; -LIST_HEAD (tmp); - -set_freezable (); - timeleft = xfs_syncd_centisecs *msecs_to_jiffies (10 ); -for (;;) { - timeleft =schedule_timeout_interruptible (timeleft); -/* swsusp */ -try_to_freeze (); -if (kthread_should_stop () &&list_empty (&mp->m_sync_list)) -break ; - -spin_lock (&mp->m_sync_lock); -/* -* We can get woken by laptop mode, to do a sync - -* that's the (only!) case where the list would be -* empty with time remaining. -*/ -if (!timeleft ||list_empty (&mp->m_sync_list)) { -if (!timeleft) - timeleft = xfs_syncd_centisecs * -msecs_to_jiffies (10 ); -INIT_LIST_HEAD (&mp->m_sync_work.w_list); -list_add_tail (&mp->m_sync_work.w_list, - &mp->m_sync_list); - } -list_for_each_entry_safe (work, n, &mp->m_sync_list, w_list) -list_move (&work->w_list, &tmp); -spin_unlock (&mp->m_sync_lock); - -list_for_each_entry_safe (work, n, &tmp, w_list) { - (*work->w_syncer)(mp, work->w_data); -list_del (&work->w_list); -if (work == &mp->m_sync_work) -continue ; -kmem_free (work); - } - } - -return 0 ; -} - STATIC voidxfs_free_fsname (struct xfs_mount *mp)
@@ -1102,8 +962,7 @@ xfs_fs_put_super(int unmount_event_flags =0 ;int error; -kthread_stop (mp->m_sync_task); - +xfs_syncd_stop (mp);xfs_sync (mp,SYNC_ATTR |SYNC_DELWRI );#ifdef HAVE_DMAPI
@@ -1753,13 +1612,9 @@ xfs_fs_fill_super( goto fail_vnrele; } - mp->m_sync_work.w_syncer = xfs_sync_worker; - mp->m_sync_work.w_mount = mp; - mp->m_sync_task =kthread_run (xfssyncd, mp,"xfssyncd" ); -if (IS_ERR (mp->m_sync_task)) { - error = -PTR_ERR (mp->m_sync_task); + error =xfs_syncd_init (mp); +if (error) goto fail_vnrele; - }xfs_itrace_exit (XFS_I (sb->s_root->d_inode));
fs/xfs/linux-2.6/xfs_super.h +0 -3
@@ -101,9 +101,6 @@ struct block_device;extern __uint64_t xfs_max_file_offset (unsigned int ); -extern void xfs_flush_inode (struct xfs_inode *); -extern void xfs_flush_device (struct xfs_inode *); -extern void xfs_blkdev_issue_flush (struct xfs_buftarg *);extern const struct export_operations xfs_export_operations;
fs/xfs/linux-2.6/xfs_sync.c +163 -0
172 lines not on this page · raw message
fs/xfs/linux-2.6/xfs_sync.h +56 -0
63 lines not on this page · raw message
fs/xfs/linux-2.6/xfs_vfs.h +0 -31
@@ -33,37 +33,6 @@ struct xfs_mount_args;typedef struct kstatfs bhv_statvfs_t ; -typedef struct bhv_vfs_sync_work { -struct list_head w_list; -struct xfs_mount *w_mount; -void *w_data;/* syncer routine argument */ -void (*w_syncer)(struct xfs_mount *,void *); -}bhv_vfs_sync_work_t ; - -#define SYNC_ATTR 0x0001/* sync attributes */ -#define SYNC_CLOSE 0x0002/* close file system down */ -#define SYNC_DELWRI 0x0004/* look at delayed writes */ -#define SYNC_WAIT 0x0008/* wait for i/o to complete */ -#define SYNC_BDFLUSH 0x0010/* BDFLUSH is calling -- don't block */ -#define SYNC_FSDATA 0x0020/* flush fs data (e.g. superblocks) */ -#define SYNC_REFCACHE 0x0040/* prune some of the nfs ref cache */ -#define SYNC_REMOUNT 0x0080/* remount readonly, no dummy LRs */ -#define SYNC_IOWAIT 0x0100/* wait for all I/O to complete */ - -/* -* When remounting a filesystem read-only or freezing the filesystem, -* we have two phases to execute. This first phase is syncing the data -* before we quiesce the fielsystem, and the second is flushing all the -* inodes out after we've waited for all the transactions created by -* the first phase to complete. The second phase uses SYNC_INODE_QUIESCE -* to ensure that the inodes are written to their location on disk -* rather than just existing in transactions in the log. This means -* after a quiesce there is no log replay required to write the inodes -* to disk (this is the main difference between a sync and a quiesce). -*/ -#define SYNC_DATA_QUIESCE (SYNC_DELWRI|SYNC_FSDATA|SYNC_WAIT|SYNC_IOWAIT) -#define SYNC_INODE_QUIESCE (SYNC_REMOUNT|SYNC_ATTR|SYNC_WAIT) -#define SHUTDOWN_META_IO_ERROR 0x0001/* write attempt to metadata failed */ #define SHUTDOWN_LOG_IO_ERROR 0x0002/* write attempt to the log failed */ #define SHUTDOWN_FORCE_UMOUNT 0x0004/* shutdown from a forced unmount */
fs/xfs/xfs_mount.h +1 -0
@@ -18,6 +18,7 @@ #ifndef __XFS_MOUNT_H__ #define __XFS_MOUNT_H__ +#include "xfs_sync.h" typedef struct xfs_trans_reservations { uint tr_write; /* extent alloc trans */
Signature · 1 line
1.5.6
PATCH 3/6 XFS: Remove xfs_iflush_all and clean up xfs_finish_reclaim_all() V3
In reply to Dave Chinnerxfs_iflush_all() walks the m_inodes list to find inodes that need reclaiming. We already have such a list - the m_del_inodes list. Replace xfs_iflush_all() with a call to xfs_finish_reclaim_all() and clean up xfs_finish_reclaim_all() to handle the different flush modes now needed.
Originally based on a patch from Christoph Hellwig.
Version 3
o rediff against new linux-2.6/xfs_sync.c code
Version 2
o revert xfs_syncsub() inode reclaim behaviour back to original code
o xfs_quiesce_fs() should use XFS_IFLUSH_DELWRI_ELSE_ASYNC, not XFS_IFLUSH_ASYNC, to prevent change of behaviour.
--- fs/xfs/linux-2.6/xfs_sync.c | 2 +- fs/xfs/xfs_inode.c | 35 ----------------------------------- fs/xfs/xfs_inode.h | 3 +-- fs/xfs/xfs_mount.c | 2 +- fs/xfs/xfs_vfsops.c | 2 +- fs/xfs/xfs_vnodeops.c | 42 ++++++++++++++++++------------------------ 6 files changed, 22 insertions(+), 64 deletions(-)
fs/xfs/linux-2.6/xfs_sync.c +1 -1
@@ -504,7 +504,7 @@ xfs_syncsub(if (flags & (SYNC_ATTR |SYNC_DELWRI )) {if (flags &SYNC_BDFLUSH ) -xfs_finish_reclaim_all (mp,1 ); +xfs_finish_reclaim_all (mp,1 ,XFS_IFLUSH_DELWRI_ELSE_ASYNC );else error =xfs_sync_inodes (mp, flags, bypassed); }
fs/xfs/xfs_inode.c +0 -35
@@ -3459,41 +3459,6 @@ corrupt_out: } -/* -* Flush all inactive inodes in mp. -*/ -void -xfs_iflush_all ( -xfs_mount_t *mp) -{ -xfs_inode_t *ip; - - again: -XFS_MOUNT_ILOCK (mp); - ip = mp->m_inodes; -if (ip ==NULL ) - goto out; - -do { -/* Make sure we skip markers inserted by sync */ -if (ip->i_mount ==NULL ) { - ip = ip->i_mnext; -continue ; - } - -if (!VFS_I (ip)) { -XFS_MOUNT_IUNLOCK (mp); -xfs_finish_reclaim (ip,0 ,XFS_IFLUSH_ASYNC ); - goto again; - } - -ASSERT (vn_count (VFS_I (ip)) ==0 ); - - ip = ip->i_mnext; - }while (ip != mp->m_inodes); - out: -XFS_MOUNT_IUNLOCK (mp); -}#ifdef XFS_ILOCK_TRACE ktrace_t *xfs_ilock_trace_buf;
fs/xfs/xfs_inode.h +1 -2
@@ -484,7 +484,7 @@ uint xfs_ilock_map_shared(xfs_inode_t *);void xfs_iunlock_map_shared (xfs_inode_t *,uint );void xfs_ireclaim (xfs_inode_t *);int xfs_finish_reclaim (xfs_inode_t *,int ,int ); -int xfs_finish_reclaim_all (struct xfs_mount *,int ); +int xfs_finish_reclaim_all (struct xfs_mount *,int ,int );/* * xfs_inode.c prototypes.
@@ -522,7 +522,6 @@ void xfs_ipin(xfs_inode_t *);void xfs_iunpin (xfs_inode_t *);int xfs_iextents_copy (xfs_inode_t *,xfs_bmbt_rec_t *,int );int xfs_iflush (xfs_inode_t *,uint ); -void xfs_iflush_all (struct xfs_mount *);void xfs_ichgtime (xfs_inode_t *,int );xfs_fsize_t xfs_file_last_byte (xfs_inode_t *);void xfs_lock_inodes (xfs_inode_t **,int ,uint );
fs/xfs/xfs_mount.c +1 -1
@@ -1241,7 +1241,7 @@ xfs_unmountfs(* need to force the log first. */ xfs_log_force (mp, (xfs_lsn_t )0 ,XFS_LOG_FORCE |XFS_LOG_SYNC ); -xfs_iflush_all (mp); +xfs_finish_reclaim_all (mp,0 ,XFS_IFLUSH_ASYNC );XFS_QM_DQPURGEALL (mp,XFS_QMOPT_QUOTALL |XFS_QMOPT_UMOUNTING );
fs/xfs/xfs_vfsops.c +1 -1
@@ -66,7 +66,7 @@ xfs_quiesce_fs(int count =0 , pincount;xfs_flush_buftarg (mp->m_ddev_targp,0 ); -xfs_finish_reclaim_all (mp,0 ); +xfs_finish_reclaim_all (mp,0 ,XFS_IFLUSH_DELWRI_ELSE_ASYNC );/* This loop must run at least twice. * The first instance of the loop will flush
fs/xfs/xfs_vnodeops.c +18 -24
54 lines not on this page · raw message
Signature · 1 line
1.5.6
PATCH 4/6 XFS: Use the inode tree for finding dirty inodes V3
In reply to Dave ChinnerUpdate xfs_sync_inodes to walk the inode radix tree cache to find dirty inodes. This removes a huge bunch of nasty, messy code for traversing the mount inode list safely and removes another user of the mount inode list.
Version 3
o rediff against new linux-2.6/xfs_sync.c code
Version 2
o add comment explaining use of gang lookups for a single inode
o use IRELE, not VN_RELE
o move check for ag initialisation to caller.
--- fs/xfs/linux-2.6/xfs_sync.c | 361 ++++++++++++------------------------------- 1 files changed, 101 insertions(+), 260 deletions(-)
fs/xfs/linux-2.6/xfs_sync.c +101 -260
@@ -121,356 +121,197 @@ xfs_sync( }/* -* xfs sync routine for internal use -* -* This routine supports all of the flags defined for the generic vfs_sync -* interface as explained above under xfs_sync. -* +* Sync all the inodes in the given AG according to the +* direction given by the flags. */ -int -xfs_sync_inodes ( +STATIC int +xfs_sync_inodes_ag (xfs_mount_t *mp, +int ag,int flags, -int *bypassed) +int *bypassed) {xfs_inode_t *ip =NULL ;struct inode *vp =NULL ; -int error; -int last_error; -uint64_t fflag; -uint lock_flags; -uint base_lock_flags; -boolean_t mount_locked; -boolean_t vnode_refed; -int preempt; -xfs_iptr_t *ipointer; -#ifdef DEBUG -boolean_t ipointer_in =B_FALSE ; - -#define IPOINTER_SET ipointer_in = B_TRUE -#define IPOINTER_CLR ipointer_in = B_FALSE -#else -#define IPOINTER_SET -#define IPOINTER_CLR -#endif - - -/* Insert a marker record into the inode list after inode ip. The list -* must be locked when this is called. After the call the list will no -* longer be locked. -*/ -#define IPOINTER_INSERT (ip, mp) { \ - ASSERT(ipointer_in == B_FALSE); \ - ipointer->ip_mnext = ip->i_mnext; \ - ipointer->ip_mprev = ip; \ - ip->i_mnext = (xfs_inode_t *)ipointer; \ - ipointer->ip_mnext->i_mprev = (xfs_inode_t *)ipointer; \ - preempt = 0; \ - XFS_MOUNT_IUNLOCK(mp); \ - mount_locked = B_FALSE; \ - IPOINTER_SET; \ - } - -/* Remove the marker from the inode list. If the marker was the only item -* in the list then there are no remaining inodes and we should zero out -* the whole list. If we are the current head of the list then move the head -* past us. -*/ -#define IPOINTER_REMOVE (ip, mp) { \ - ASSERT(ipointer_in == B_TRUE); \ - if (ipointer->ip_mnext != (xfs_inode_t *)ipointer) { \ - ip = ipointer->ip_mnext; \ - ip->i_mprev = ipointer->ip_mprev; \ - ipointer->ip_mprev->i_mnext = ip; \ - if (mp->m_inodes == (xfs_inode_t *)ipointer) { \ - mp->m_inodes = ip; \ - } \ - } else { \ - ASSERT(mp->m_inodes == (xfs_inode_t *)ipointer); \ - mp->m_inodes = NULL; \ - ip = NULL; \ - } \ - IPOINTER_CLR; \ - } - -#define XFS_PREEMPT_MASK 0x7f - -ASSERT (!(flags &SYNC_BDFLUSH )); - -if (bypassed) - *bypassed =0 ; -if (mp->m_flags &XFS_MOUNT_RDONLY ) -return 0 ; - error =0 ; - last_error =0 ; - preempt =0 ; - -/* Allocate a reference marker */ - ipointer = (xfs_iptr_t *)kmem_zalloc (sizeof (xfs_iptr_t),KM_SLEEP ); +xfs_perag_t *pag = &mp->m_perag[ag]; +boolean_t vnode_refed =B_FALSE ; +int nr_found; +int first_index =0 ; +int error =0 ; +int last_error =0 ; +int fflag =XFS_B_ASYNC ; +int lock_flags =XFS_ILOCK_SHARED ; - fflag =XFS_B_ASYNC ;/* default is don't wait */ if (flags &SYNC_DELWRI ) fflag =XFS_B_DELWRI ;if (flags &SYNC_WAIT ) fflag =0 ;/* synchronous overrides all */ - base_lock_flags =XFS_ILOCK_SHARED ;if (flags & (SYNC_DELWRI |SYNC_CLOSE )) {/* * We need the I/O lock if we're going to call any of * the flush/inval routines. */ - base_lock_flags |=XFS_IOLOCK_SHARED ; + lock_flags |=XFS_IOLOCK_SHARED ; } -XFS_MOUNT_ILOCK (mp); - - ip = mp->m_inodes; - - mount_locked =B_TRUE ; - vnode_refed =B_FALSE ; - -IPOINTER_CLR ; -do { -ASSERT (ipointer_in ==B_FALSE ); -ASSERT (vnode_refed ==B_FALSE ); - - lock_flags = base_lock_flags; -/* -* There were no inodes in the list, just break out -* of the loop. +* use a gang lookup to find the next inode in the tree +* as the tree is sparse and a gang lookup walks to find +* the number of objects requested. */ -if (ip ==NULL ) { -break ; - } +read_lock (&pag->pag_ici_lock); + nr_found =radix_tree_gang_lookup (&pag->pag_ici_root, + (void **)&ip, first_index,1 ); -/* -* We found another sync thread marker - skip it -*/ -if (ip->i_mount ==NULL ) { - ip = ip->i_mnext; -continue ; +if (!nr_found) { +read_unlock (&pag->pag_ici_lock); +break ; } - vp =VFS_I (ip); +/* update the index for the next lookup */ + first_index =XFS_INO_TO_AGINO (mp, ip->i_ino +1 );/* -* If the vnode is gone then this is being torn down, -* call reclaim if it is flushed, else let regular flush -* code deal with it later in the loop. +* skip inodes in reclaim. Let xfs_syncsub do that for +* us so we don't need to worry. */ - -if (vp ==NULL ) { -/* Skip ones already in reclaim */ -if (ip->i_flags &XFS_IRECLAIM ) { - ip = ip->i_mnext; -continue ; - } -if (xfs_ilock_nowait (ip,XFS_ILOCK_EXCL ) ==0 ) { - ip = ip->i_mnext; - }else if ((xfs_ipincount (ip) ==0 ) && -xfs_iflock_nowait (ip)) { -IPOINTER_INSERT (ip, mp); - -xfs_finish_reclaim (ip,1 , -XFS_IFLUSH_DELWRI_ELSE_ASYNC ); - -XFS_MOUNT_ILOCK (mp); - mount_locked =B_TRUE ; -IPOINTER_REMOVE (ip, mp); - }else { -xfs_iunlock (ip,XFS_ILOCK_EXCL ); - ip = ip->i_mnext; - } + vp =VFS_I (ip); +if (!vp) { +read_unlock (&pag->pag_ici_lock);continue ; } +/* bad inodes are dealt with elsewhere */ if (VN_BAD (vp)) { - ip = ip->i_mnext; +read_unlock (&pag->pag_ici_lock);continue ; } +/* nothing to sync during shutdown */ if (XFS_FORCED_SHUTDOWN (mp) && !(flags &SYNC_CLOSE )) { -XFS_MOUNT_IUNLOCK (mp); -kmem_free (ipointer); +read_unlock (&pag->pag_ici_lock);return 0 ; }/* -* Try to lock without sleeping. We're out of order with -* the inode list lock here, so if we fail we need to drop -* the mount lock and try again. If we're called from -* bdflush() here, then don't bother. -* -* The inode lock here actually coordinates with the -* almost spurious inode lock in xfs_ireclaim() to prevent -* the vnode we handle here without a reference from -* being freed while we reference it. If we lock the inode -* while it's on the mount list here, then the spurious inode -* lock in xfs_ireclaim() after the inode is pulled from -* the mount list will sleep until we release it here. -* This keeps the vnode from being freed while we reference -* it. +* The inode lock here actually coordinates with the almost +* spurious inode lock in xfs_ireclaim() to prevent the vnode +* we handle here without a reference from being freed while we +* reference it. If we lock the inode while it's on the mount +* list here, then the spurious inode lock in xfs_ireclaim() +* after the inode is pulled from the mount list will sleep +* until we release it here. This keeps the vnode from being +* freed while we reference it. */ if (xfs_ilock_nowait (ip, lock_flags) ==0 ) { -if (vp ==NULL ) { - ip = ip->i_mnext; -continue ; - } - vp =vn_grab (vp); -if (vp ==NULL ) { - ip = ip->i_mnext; +read_unlock (&pag->pag_ici_lock); +if (!vp)continue ; - } - -IPOINTER_INSERT (ip, mp);xfs_ilock (ip, lock_flags);ASSERT (vp ==VFS_I (ip));ASSERT (ip->i_mount == mp); vnode_refed =B_TRUE ; + }else { +/* safe to unlock here as we have a reference */ +read_unlock (&pag->pag_ici_lock); } - -/* From here on in the loop we may have a marker record -* in the inode list. -*/ -/* * If we have to flush data or wait for I/O completion * we need to drop the ilock that we currently hold. * If we need to drop the lock, insert a marker if we * have not already done so. */ -if ((flags & (SYNC_CLOSE |SYNC_IOWAIT )) || - ((flags &SYNC_DELWRI ) &&VN_DIRTY (vp))) { -if (mount_locked) { -IPOINTER_INSERT (ip, mp); - } +if (flags &SYNC_CLOSE ) {xfs_iunlock (ip,XFS_ILOCK_SHARED ); - -if (flags &SYNC_CLOSE ) { -/* Shutdown case. Flush and invalidate. */ -if (XFS_FORCED_SHUTDOWN (mp)) -xfs_tosspages (ip,0 ,-1 , -FI_REMAPF ); -else - error =xfs_flushinval_pages (ip, -0 ,-1 ,FI_REMAPF ); - }else if ((flags &SYNC_DELWRI ) &&VN_DIRTY (vp)) { - error =xfs_flush_pages (ip,0 , --1 , fflag,FI_NONE ); - } - -/* -* When freezing, we need to wait ensure all I/O (including direct -* I/O) is complete to ensure no further data modification can take -* place after this point -*/ +if (XFS_FORCED_SHUTDOWN (mp)) +xfs_tosspages (ip,0 ,-1 ,FI_REMAPF ); +else + error =xfs_flushinval_pages (ip,0 ,-1 , +FI_REMAPF ); +/* wait for I/O on freeze */ if (flags &SYNC_IOWAIT )vn_iowait (ip);xfs_ilock (ip,XFS_ILOCK_SHARED ); } -if ((flags &SYNC_ATTR ) && - (ip->i_update_core || - (ip->i_itemp && ip->i_itemp->ili_format.ilf_fields))) { -if (mount_locked) -IPOINTER_INSERT (ip, mp); +if ((flags &SYNC_DELWRI ) &&VN_DIRTY (vp)) { +xfs_iunlock (ip,XFS_ILOCK_SHARED ); + error =xfs_flush_pages (ip,0 ,-1 , fflag,FI_NONE ); +if (flags &SYNC_IOWAIT ) +vn_iowait (ip); +xfs_ilock (ip,XFS_ILOCK_SHARED ); + } +if ((flags &SYNC_ATTR ) && !xfs_inode_clean (ip)) {if (flags &SYNC_WAIT ) {xfs_iflock (ip); - error =xfs_iflush (ip,XFS_IFLUSH_SYNC ); - -/* -* If we can't acquire the flush lock, then the inode -* is already being flushed so don't bother waiting. -* -* If we can lock it then do a delwri flush so we can -* combine multiple inode flushes in each disk write. -*/ +if (!xfs_inode_clean (ip)) + error =xfs_iflush (ip,XFS_IFLUSH_SYNC ); +else +xfs_ifunlock (ip); }else if (xfs_iflock_nowait (ip)) { - error =xfs_iflush (ip,XFS_IFLUSH_DELWRI ); +if (!xfs_inode_clean (ip)) + error =xfs_iflush (ip,XFS_IFLUSH_DELWRI ); +else +xfs_ifunlock (ip); }else if (bypassed) { (*bypassed)++; } } -if (lock_flags !=0 ) { +if (lock_flags)xfs_iunlock (ip, lock_flags); - }if (vnode_refed) { -/* -* If we had to take a reference on the vnode -* above, then wait until after we've unlocked -* the inode to release the reference. This is -* because we can be already holding the inode -* lock when IRELE() calls xfs_inactive(). -* -* Make sure to drop the mount lock before calling -* IRELE() so that we don't trip over ourselves if -* we have to go for the mount lock again in the -* inactive code. -*/ -if (mount_locked) { -IPOINTER_INSERT (ip, mp); - } -IRELE (ip); - vnode_refed =B_FALSE ; } -if (error) { +if (error) last_error = error; - } -/* * bail out if the filesystem is corrupted. */ -if (error ==EFSCORRUPTED ) { -if (!mount_locked) { -XFS_MOUNT_ILOCK (mp); -IPOINTER_REMOVE (ip, mp); - } -XFS_MOUNT_IUNLOCK (mp); -ASSERT (ipointer_in ==B_FALSE ); -kmem_free (ipointer); +if (error ==EFSCORRUPTED )return XFS_ERROR (error); - } - -/* Let other threads have a chance at the mount lock -* if we have looped many times without dropping the -* lock. -*/ -if ((++preempt &XFS_PREEMPT_MASK ) ==0 ) { -if (mount_locked) { -IPOINTER_INSERT (ip, mp); - } - } - -if (mount_locked ==B_FALSE ) { -XFS_MOUNT_ILOCK (mp); - mount_locked =B_TRUE ; -IPOINTER_REMOVE (ip, mp); -continue ; - } -ASSERT (ipointer_in ==B_FALSE ); - ip = ip->i_mnext; + }while (nr_found); - }while (ip != mp->m_inodes); +return last_error; +} -XFS_MOUNT_IUNLOCK (mp); +int +xfs_sync_inodes ( +xfs_mount_t *mp, +int flags, +int *bypassed) +{ +int error; +int last_error; +int i; -ASSERT (ipointer_in ==B_FALSE ); +if (bypassed) + *bypassed =0 ; +if (mp->m_flags &XFS_MOUNT_RDONLY ) +return 0 ; + error =0 ; + last_error =0 ; -kmem_free (ipointer); +for (i =0 ; i < mp->m_sb.sb_agcount; i++) { +if (!mp->m_perag[i].pag_ici_init) +continue ; + error =xfs_sync_inodes_ag (mp, i, flags, bypassed); +if (error) + last_error = error; +if (error ==EFSCORRUPTED ) +break ; + }return XFS_ERROR (last_error); }
Signature · 1 line
1.5.6
PATCH 5/6 XFS: Traverse inode trees when releasing dquots V3
In reply to Dave ChinnerMake releasing all inode dquots traverse the per-ag inode radix trees rather than the mount inode list. This removes another user of the mount inode list.
Version 3
o fix comment relating to avoiding trying to release the quota inodes and those in reclaim.
Version 2
o add comment explaining use of gang lookups for a single inode
o use IRELE, not VN_RELE
o move check for ag initialisation to caller.
--- fs/xfs/quota/xfs_qm_syscalls.c | 127 ++++++++++++++++++--------------------- 1 files changed, 59 insertions(+), 68 deletions(-)
fs/xfs/quota/xfs_qm_syscalls.c +59 -68
@@ -1022,101 +1022,92 @@ xfs_qm_export_flags(/* -* Go thru all the inodes in the file system, releasing their dquots. -* Note that the mount structure gets modified to indicate that quotas are off -* AFTER this, in the case of quotaoff. This also gets called from -* xfs_rootumount. +* Release all the dquots on the inodes in an AG. */ -void -xfs_qm_dqrele_all_inodes ( -struct xfs_mount *mp, -uint flags) +STATIC void +xfs_qm_dqrele_inodes_ag ( +xfs_mount_t *mp, +int ag, +uint flags) { -xfs_inode_t *ip, *topino; -uint ireclaims; -struct inode *vp; -boolean_t vnode_refd; +xfs_inode_t *ip =NULL ; +struct inode *vp =NULL ; +xfs_perag_t *pag = &mp->m_perag[ag]; +int first_index =0 ; +int nr_found; -ASSERT (mp->m_quotainfo); - -XFS_MOUNT_ILOCK (mp); -again: - ip = mp->m_inodes; -if (ip ==NULL ) { -XFS_MOUNT_IUNLOCK (mp); -return ; - }do { -/* Skip markers inserted by xfs_sync */ -if (ip->i_mount ==NULL ) { - ip = ip->i_mnext; -continue ; - } -/* Root inode, rbmip and rsumip have associated blocks */ -if (ip ==XFS_QI_UQIP (mp) || ip ==XFS_QI_GQIP (mp)) { -ASSERT (ip->i_udquot ==NULL ); -ASSERT (ip->i_gdquot ==NULL ); - ip = ip->i_mnext; -continue ; +boolean_t vnode_refd =B_FALSE ; + +/* +* use a gang lookup to find the next inode in the tree +* as the tree is sparse and a gang lookup walks to find +* the number of objects requested. +*/ +read_lock (&pag->pag_ici_lock); + nr_found =radix_tree_gang_lookup (&pag->pag_ici_root, + (void **)&ip, first_index,1 ); + +if (!nr_found) { +read_unlock (&pag->pag_ici_lock); +break ; } + +/* update the index for the next lookup */ + first_index =XFS_INO_TO_AGINO (mp, ip->i_ino +1 ); + +/* skip quota inodes and those in reclaim */ vp =VFS_I (ip); -if (!vp) { +if (!vp || ip ==XFS_QI_UQIP (mp) || ip ==XFS_QI_GQIP (mp)) {ASSERT (ip->i_udquot ==NULL );ASSERT (ip->i_gdquot ==NULL ); - ip = ip->i_mnext; +read_unlock (&pag->pag_ici_lock);continue ; } - vnode_refd =B_FALSE ;if (xfs_ilock_nowait (ip,XFS_ILOCK_EXCL ) ==0 ) { - ireclaims = mp->m_ireclaims; - topino = mp->m_inodes; vp =vn_grab (vp); +read_unlock (&pag->pag_ici_lock);if (!vp) - goto again; - -XFS_MOUNT_IUNLOCK (mp); -/* XXX restart limit ? */ -xfs_ilock (ip,XFS_ILOCK_EXCL ); +continue ; vnode_refd =B_TRUE ; +xfs_ilock (ip,XFS_ILOCK_EXCL ); }else { - ireclaims = mp->m_ireclaims; - topino = mp->m_inodes; -XFS_MOUNT_IUNLOCK (mp); +read_unlock (&pag->pag_ici_lock); } - -/* -* We don't keep the mountlock across the dqrele() call, -* since it can take a while.. -*/ if ((flags &XFS_UQUOTA_ACCT ) && ip->i_udquot) {xfs_qm_dqrele (ip->i_udquot); ip->i_udquot =NULL ; } -if (flags & (XFS_PQUOTA_ACCT |XFS_GQUOTA_ACCT ) && ip->i_gdquot) { +if (flags & (XFS_PQUOTA_ACCT |XFS_GQUOTA_ACCT ) && + ip->i_gdquot) {xfs_qm_dqrele (ip->i_gdquot); ip->i_gdquot =NULL ; }xfs_iunlock (ip,XFS_ILOCK_EXCL ); -/* -* Wait until we've dropped the ilock and mountlock to -* do the vn_rele. Or be condemned to an eternity in the -* inactive code in hell. -*/ if (vnode_refd)IRELE (ip); -XFS_MOUNT_ILOCK (mp); -/* -* If an inode was inserted or removed, we gotta -* start over again. -*/ -if (topino != mp->m_inodes || mp->m_ireclaims != ireclaims) { -/* XXX use a sentinel */ - goto again; - } - ip = ip->i_mnext; - }while (ip != mp->m_inodes); + }while (nr_found); +} -XFS_MOUNT_IUNLOCK (mp); +/* +* Go thru all the inodes in the file system, releasing their dquots. +* Note that the mount structure gets modified to indicate that quotas are off +* AFTER this, in the case of quotaoff. This also gets called from +* xfs_rootumount. +*/ +void +xfs_qm_dqrele_all_inodes ( +struct xfs_mount *mp, +uint flags) +{ +int i; + +ASSERT (mp->m_quotainfo); +for (i =0 ; i < mp->m_sb.sb_agcount; i++) { +if (!mp->m_perag[i].pag_ici_init) +continue ; +xfs_qm_dqrele_inodes_ag (mp, i, flags); + } }/*------------------------------------------------------------------------*/
Signature · 1 line
1.5.6
PATCH 6/6 XFS: remove the mount inode list
In reply to Dave ChinnerNow we've removed all users of the mount inode list, we can kill it. This reduces the size of the xfs_inode by 2 pointers.
--- fs/xfs/xfs_iget.c | 42 +----------------------------------------- fs/xfs/xfs_inode.h | 8 -------- fs/xfs/xfs_mount.c | 5 ----- fs/xfs/xfs_mount.h | 1 - 4 files changed, 1 insertions(+), 55 deletions(-)
fs/xfs/xfs_iget.c +1 -41
@@ -76,7 +76,6 @@ xfs_iget_core( {struct inode *old_inode;xfs_inode_t *ip; -xfs_inode_t *iq;int error;unsigned long first_index, mask;xfs_perag_t *pag;
@@ -267,24 +266,6 @@ finish_inode:write_unlock (&pag->pag_ici_lock);radix_tree_preload_end (); - -/* -* Link ip to its mount and thread it on the mount's inode list. -*/ -XFS_MOUNT_ILOCK (mp); -if ((iq = mp->m_inodes)) { -ASSERT (iq->i_mprev->i_mnext == iq); - ip->i_mprev = iq->i_mprev; - iq->i_mprev->i_mnext = ip; - iq->i_mprev = ip; - ip->i_mnext = iq; - }else { - ip->i_mnext = ip; - ip->i_mprev = ip; - } - mp->m_inodes = ip; - -XFS_MOUNT_IUNLOCK (mp);xfs_put_perag (mp, pag); return_ip:
@@ -505,36 +486,15 @@ xfs_iextract( {xfs_mount_t *mp = ip->i_mount;xfs_perag_t *pag =xfs_get_perag (mp, ip->i_ino); -xfs_inode_t *iq;write_lock (&pag->pag_ici_lock);radix_tree_delete (&pag->pag_ici_root,XFS_INO_TO_AGINO (mp, ip->i_ino));write_unlock (&pag->pag_ici_lock);xfs_put_perag (mp, pag); -/* -* Remove from mount's inode list. -*/ -XFS_MOUNT_ILOCK (mp); -ASSERT ((ip->i_mnext !=NULL ) && (ip->i_mprev !=NULL )); - iq = ip->i_mnext; - iq->i_mprev = ip->i_mprev; - ip->i_mprev->i_mnext = iq; - -/* -* Fix up the head pointer if it points to the inode being deleted. -*/ -if (mp->m_inodes == ip) { -if (ip == iq) { - mp->m_inodes =NULL ; - }else { - mp->m_inodes = iq; - } - } -/* Deal with the deleted inodes list */ +XFS_MOUNT_ILOCK (mp);list_del_init (&ip->i_reclaim); - mp->m_ireclaims++;XFS_MOUNT_IUNLOCK (mp); }
fs/xfs/xfs_inode.h +0 -8
@@ -191,16 +191,8 @@ typedef struct xfs_icdinode {__uint32_t di_gen;/* generation number */ } xfs_icdinode_t; -typedef struct { -struct xfs_inode *ip_mnext;/* next inode in mount list */ -struct xfs_inode *ip_mprev;/* ptr to prev inode */ -struct xfs_mount *ip_mount;/* fs mount struct ptr */ -}xfs_iptr_t ; - typedef struct xfs_inode { /* Inode linking and identification information. */ - struct xfs_inode *i_mnext; /* next inode in mount list */ - struct xfs_inode *i_mprev; /* ptr to prev inode */ struct xfs_mount *i_mount; /* fs mount struct ptr */ struct list_head i_reclaim; /* reclaim list */ struct inode *i_vnode; /* vnode backpointer */
fs/xfs/xfs_mount.c +0 -5
@@ -1285,11 +1285,6 @@ xfs_unmountfs(xfs_unmountfs_wait (mp);/* wait for async bufs */ xfs_log_unmount (mp);/* Done! No more fs ops. */ -/* -* All inodes from this mount point should be freed. -*/ -ASSERT (mp->m_inodes ==NULL ); -if ((mp->m_flags &XFS_MOUNT_NOUUID ) ==0 )uuid_table_remove (&mp->m_sb.sb_uuid);
fs/xfs/xfs_mount.h +0 -1
@@ -248,7 +248,6 @@ typedef struct xfs_mount {xfs_agnumber_t m_agirotor;/* last ag dir inode alloced */ spinlock_t m_agirotor_lock;/* .. and lock protecting it */ xfs_agnumber_t m_maxagi;/* highest inode alloc group */ -struct xfs_inode *m_inodes;/* active inode list */ struct list_head m_del_inodes;/* inodes to reclaim */ mutex_t m_ilock;/* inode list mutex */ uint m_ireclaims;/* count of calls to reclaim*/
Signature · 1 line
1.5.6
Quoted 2 lines · Dave Chinner
o revert
xfs_syncsub->xfs_syncchange inxfs_quiesce_fsand rediff patch series
So no changes to the last version posted, and you still have my ACK for it..