]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: hoist xfs_iunlink to libxfs
authorDarrick J. Wong <djwong@kernel.org>
Tue, 2 Jul 2024 18:22:40 +0000 (11:22 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 2 Jul 2024 18:36:58 +0000 (11:36 -0700)
Move xfs_iunlink and xfs_iunlink_remove to libxfs.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_inode_util.c
fs/xfs/libxfs/xfs_inode_util.h
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h

index 8654cb3d79efb7b3364cf4524900ab39006d756b..5739871ac3705e329b14bf87cdd5956ae9e2efcd 100644 (file)
 #include "xfs_ialloc.h"
 #include "xfs_health.h"
 #include "xfs_bmap.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_ag.h"
+#include "xfs_iunlink_item.h"
 
 uint16_t
 xfs_flags2diflags(
@@ -344,3 +348,281 @@ xfs_inode_init(
 
        xfs_trans_log_inode(tp, ip, flags);
 }
+
+/*
+ * In-Core Unlinked List Lookups
+ * =============================
+ *
+ * Every inode is supposed to be reachable from some other piece of metadata
+ * with the exception of the root directory.  Inodes with a connection to a
+ * file descriptor but not linked from anywhere in the on-disk directory tree
+ * are collectively known as unlinked inodes, though the filesystem itself
+ * maintains links to these inodes so that on-disk metadata are consistent.
+ *
+ * XFS implements a per-AG on-disk hash table of unlinked inodes.  The AGI
+ * header contains a number of buckets that point to an inode, and each inode
+ * record has a pointer to the next inode in the hash chain.  This
+ * singly-linked list causes scaling problems in the iunlink remove function
+ * because we must walk that list to find the inode that points to the inode
+ * being removed from the unlinked hash bucket list.
+ *
+ * Hence we keep an in-memory double linked list to link each inode on an
+ * unlinked list. Because there are 64 unlinked lists per AGI, keeping pointer
+ * based lists would require having 64 list heads in the perag, one for each
+ * list. This is expensive in terms of memory (think millions of AGs) and cache
+ * misses on lookups. Instead, use the fact that inodes on the unlinked list
+ * must be referenced at the VFS level to keep them on the list and hence we
+ * have an existence guarantee for inodes on the unlinked list.
+ *
+ * Given we have an existence guarantee, we can use lockless inode cache lookups
+ * to resolve aginos to xfs inodes. This means we only need 8 bytes per inode
+ * for the double linked unlinked list, and we don't need any extra locking to
+ * keep the list safe as all manipulations are done under the AGI buffer lock.
+ * Keeping the list up to date does not require memory allocation, just finding
+ * the XFS inode and updating the next/prev unlinked list aginos.
+ */
+
+/*
+ * Update the prev pointer of the next agino.  Returns -ENOLINK if the inode
+ * is not in cache.
+ */
+static int
+xfs_iunlink_update_backref(
+       struct xfs_perag        *pag,
+       xfs_agino_t             prev_agino,
+       xfs_agino_t             next_agino)
+{
+       struct xfs_inode        *ip;
+
+       /* No update necessary if we are at the end of the list. */
+       if (next_agino == NULLAGINO)
+               return 0;
+
+       ip = xfs_iunlink_lookup(pag, next_agino);
+       if (!ip)
+               return -ENOLINK;
+
+       ip->i_prev_unlinked = prev_agino;
+       return 0;
+}
+
+/*
+ * Point the AGI unlinked bucket at an inode and log the results.  The caller
+ * is responsible for validating the old value.
+ */
+STATIC int
+xfs_iunlink_update_bucket(
+       struct xfs_trans        *tp,
+       struct xfs_perag        *pag,
+       struct xfs_buf          *agibp,
+       unsigned int            bucket_index,
+       xfs_agino_t             new_agino)
+{
+       struct xfs_agi          *agi = agibp->b_addr;
+       xfs_agino_t             old_value;
+       int                     offset;
+
+       ASSERT(xfs_verify_agino_or_null(pag, new_agino));
+
+       old_value = be32_to_cpu(agi->agi_unlinked[bucket_index]);
+       trace_xfs_iunlink_update_bucket(tp->t_mountp, pag->pag_agno, bucket_index,
+                       old_value, new_agino);
+
+       /*
+        * We should never find the head of the list already set to the value
+        * passed in because either we're adding or removing ourselves from the
+        * head of the list.
+        */
+       if (old_value == new_agino) {
+               xfs_buf_mark_corrupt(agibp);
+               xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
+               return -EFSCORRUPTED;
+       }
+
+       agi->agi_unlinked[bucket_index] = cpu_to_be32(new_agino);
+       offset = offsetof(struct xfs_agi, agi_unlinked) +
+                       (sizeof(xfs_agino_t) * bucket_index);
+       xfs_trans_log_buf(tp, agibp, offset, offset + sizeof(xfs_agino_t) - 1);
+       return 0;
+}
+
+static int
+xfs_iunlink_insert_inode(
+       struct xfs_trans        *tp,
+       struct xfs_perag        *pag,
+       struct xfs_buf          *agibp,
+       struct xfs_inode        *ip)
+{
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_agi          *agi = agibp->b_addr;
+       xfs_agino_t             next_agino;
+       xfs_agino_t             agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
+       short                   bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS;
+       int                     error;
+
+       /*
+        * Get the index into the agi hash table for the list this inode will
+        * go on.  Make sure the pointer isn't garbage and that this inode
+        * isn't already on the list.
+        */
+       next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]);
+       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;
+       }
+
+       /*
+        * Update the prev pointer in the next inode to point back to this
+        * inode.
+        */
+       error = xfs_iunlink_update_backref(pag, agino, next_agino);
+       if (error == -ENOLINK)
+               error = xfs_iunlink_reload_next(tp, agibp, agino, next_agino);
+       if (error)
+               return error;
+
+       if (next_agino != NULLAGINO) {
+               /*
+                * There is already another inode in the bucket, so point this
+                * inode to the current head of the list.
+                */
+               error = xfs_iunlink_log_inode(tp, ip, pag, next_agino);
+               if (error)
+                       return error;
+               ip->i_next_unlinked = next_agino;
+       }
+
+       /* Point the head of the list to point to this inode. */
+       ip->i_prev_unlinked = NULLAGINO;
+       return xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, agino);
+}
+
+/*
+ * This is called when the inode's link count has gone to 0 or we are creating
+ * a tmpfile via O_TMPFILE.  The inode @ip must have nlink == 0.
+ *
+ * We place the on-disk inode on a list in the AGI.  It will be pulled from this
+ * list when the inode is freed.
+ */
+int
+xfs_iunlink(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip)
+{
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_perag        *pag;
+       struct xfs_buf          *agibp;
+       int                     error;
+
+       ASSERT(VFS_I(ip)->i_nlink == 0);
+       ASSERT(VFS_I(ip)->i_mode != 0);
+       trace_xfs_iunlink(ip);
+
+       pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
+
+       /* Get the agi buffer first.  It ensures lock ordering on the list. */
+       error = xfs_read_agi(pag, tp, 0, &agibp);
+       if (error)
+               goto out;
+
+       error = xfs_iunlink_insert_inode(tp, pag, agibp, ip);
+out:
+       xfs_perag_put(pag);
+       return error;
+}
+
+static int
+xfs_iunlink_remove_inode(
+       struct xfs_trans        *tp,
+       struct xfs_perag        *pag,
+       struct xfs_buf          *agibp,
+       struct xfs_inode        *ip)
+{
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_agi          *agi = agibp->b_addr;
+       xfs_agino_t             agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
+       xfs_agino_t             head_agino;
+       short                   bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS;
+       int                     error;
+
+       trace_xfs_iunlink_remove(ip);
+
+       /*
+        * Get the index into the agi hash table for the list this inode will
+        * go on.  Make sure the head pointer isn't garbage.
+        */
+       head_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]);
+       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;
+       }
+
+       /*
+        * Set our inode's next_unlinked pointer to NULL and then return
+        * the old pointer value so that we can update whatever was previous
+        * to us in the list to point to whatever was next in the list.
+        */
+       error = xfs_iunlink_log_inode(tp, ip, pag, NULLAGINO);
+       if (error)
+               return error;
+
+       /*
+        * Update the prev pointer in the next inode to point back to previous
+        * inode in the chain.
+        */
+       error = xfs_iunlink_update_backref(pag, ip->i_prev_unlinked,
+                       ip->i_next_unlinked);
+       if (error == -ENOLINK)
+               error = xfs_iunlink_reload_next(tp, agibp, ip->i_prev_unlinked,
+                               ip->i_next_unlinked);
+       if (error)
+               return error;
+
+       if (head_agino != agino) {
+               struct xfs_inode        *prev_ip;
+
+               prev_ip = xfs_iunlink_lookup(pag, ip->i_prev_unlinked);
+               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);
+               prev_ip->i_next_unlinked = ip->i_next_unlinked;
+       } else {
+               /* Point the head of the list to the next unlinked inode. */
+               error = xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index,
+                               ip->i_next_unlinked);
+       }
+
+       ip->i_next_unlinked = NULLAGINO;
+       ip->i_prev_unlinked = 0;
+       return error;
+}
+
+/*
+ * Pull the on-disk inode from the AGI unlinked list.
+ */
+int
+xfs_iunlink_remove(
+       struct xfs_trans        *tp,
+       struct xfs_perag        *pag,
+       struct xfs_inode        *ip)
+{
+       struct xfs_buf          *agibp;
+       int                     error;
+
+       trace_xfs_iunlink_remove(ip);
+
+       /* Get the agi buffer first.  It ensures lock ordering on the list. */
+       error = xfs_read_agi(pag, tp, 0, &agibp);
+       if (error)
+               return error;
+
+       return xfs_iunlink_remove_inode(tp, pag, agibp, ip);
+}
index bf5393db4fde61712ab236997a5fddbeebcd99ee..42a032afe3cacebe52acc7ce774f38e76ea915b2 100644 (file)
@@ -47,4 +47,8 @@ void xfs_trans_ichgtime(struct xfs_trans *tp, struct xfs_inode *ip, int flags);
 void xfs_inode_init(struct xfs_trans *tp, const struct xfs_icreate_args *args,
                struct xfs_inode *ip);
 
+int xfs_iunlink(struct xfs_trans *tp, struct xfs_inode *ip);
+int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
+               struct xfs_inode *ip);
+
 #endif /* __XFS_INODE_UTIL_H__ */
index 5848f7b36cc562b79f798bf867f82a21ed982739..ac5826ce5fece06e41407e89e55b59ead2422175 100644 (file)
@@ -1738,39 +1738,6 @@ out:
        return error;
 }
 
-/*
- * In-Core Unlinked List Lookups
- * =============================
- *
- * Every inode is supposed to be reachable from some other piece of metadata
- * with the exception of the root directory.  Inodes with a connection to a
- * file descriptor but not linked from anywhere in the on-disk directory tree
- * are collectively known as unlinked inodes, though the filesystem itself
- * maintains links to these inodes so that on-disk metadata are consistent.
- *
- * XFS implements a per-AG on-disk hash table of unlinked inodes.  The AGI
- * header contains a number of buckets that point to an inode, and each inode
- * record has a pointer to the next inode in the hash chain.  This
- * singly-linked list causes scaling problems in the iunlink remove function
- * because we must walk that list to find the inode that points to the inode
- * being removed from the unlinked hash bucket list.
- *
- * Hence we keep an in-memory double linked list to link each inode on an
- * unlinked list. Because there are 64 unlinked lists per AGI, keeping pointer
- * based lists would require having 64 list heads in the perag, one for each
- * list. This is expensive in terms of memory (think millions of AGs) and cache
- * misses on lookups. Instead, use the fact that inodes on the unlinked list
- * must be referenced at the VFS level to keep them on the list and hence we
- * have an existence guarantee for inodes on the unlinked list.
- *
- * Given we have an existence guarantee, we can use lockless inode cache lookups
- * to resolve aginos to xfs inodes. This means we only need 8 bytes per inode
- * for the double linked unlinked list, and we don't need any extra locking to
- * keep the list safe as all manipulations are done under the AGI buffer lock.
- * Keeping the list up to date does not require memory allocation, just finding
- * the XFS inode and updating the next/prev unlinked list aginos.
- */
-
 /*
  * Find an inode on the unlinked list. This does not take references to the
  * inode as we have existence guarantees by holding the AGI buffer lock and that
@@ -1805,76 +1772,12 @@ xfs_iunlink_lookup(
        return ip;
 }
 
-/*
- * Update the prev pointer of the next agino.  Returns -ENOLINK if the inode
- * is not in cache.
- */
-static int
-xfs_iunlink_update_backref(
-       struct xfs_perag        *pag,
-       xfs_agino_t             prev_agino,
-       xfs_agino_t             next_agino)
-{
-       struct xfs_inode        *ip;
-
-       /* No update necessary if we are at the end of the list. */
-       if (next_agino == NULLAGINO)
-               return 0;
-
-       ip = xfs_iunlink_lookup(pag, next_agino);
-       if (!ip)
-               return -ENOLINK;
-
-       ip->i_prev_unlinked = prev_agino;
-       return 0;
-}
-
-/*
- * Point the AGI unlinked bucket at an inode and log the results.  The caller
- * is responsible for validating the old value.
- */
-STATIC int
-xfs_iunlink_update_bucket(
-       struct xfs_trans        *tp,
-       struct xfs_perag        *pag,
-       struct xfs_buf          *agibp,
-       unsigned int            bucket_index,
-       xfs_agino_t             new_agino)
-{
-       struct xfs_agi          *agi = agibp->b_addr;
-       xfs_agino_t             old_value;
-       int                     offset;
-
-       ASSERT(xfs_verify_agino_or_null(pag, new_agino));
-
-       old_value = be32_to_cpu(agi->agi_unlinked[bucket_index]);
-       trace_xfs_iunlink_update_bucket(tp->t_mountp, pag->pag_agno, bucket_index,
-                       old_value, new_agino);
-
-       /*
-        * We should never find the head of the list already set to the value
-        * passed in because either we're adding or removing ourselves from the
-        * head of the list.
-        */
-       if (old_value == new_agino) {
-               xfs_buf_mark_corrupt(agibp);
-               xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
-               return -EFSCORRUPTED;
-       }
-
-       agi->agi_unlinked[bucket_index] = cpu_to_be32(new_agino);
-       offset = offsetof(struct xfs_agi, agi_unlinked) +
-                       (sizeof(xfs_agino_t) * bucket_index);
-       xfs_trans_log_buf(tp, agibp, offset, offset + sizeof(xfs_agino_t) - 1);
-       return 0;
-}
-
 /*
  * Load the inode @next_agino into the cache and set its prev_unlinked pointer
  * to @prev_agino.  Caller must hold the AGI to synchronize with other changes
  * to the unlinked list.
  */
-STATIC int
+int
 xfs_iunlink_reload_next(
        struct xfs_trans        *tp,
        struct xfs_buf          *agibp,
@@ -1930,187 +1833,6 @@ rele:
        return error;
 }
 
-static int
-xfs_iunlink_insert_inode(
-       struct xfs_trans        *tp,
-       struct xfs_perag        *pag,
-       struct xfs_buf          *agibp,
-       struct xfs_inode        *ip)
-{
-       struct xfs_mount        *mp = tp->t_mountp;
-       struct xfs_agi          *agi = agibp->b_addr;
-       xfs_agino_t             next_agino;
-       xfs_agino_t             agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
-       short                   bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS;
-       int                     error;
-
-       /*
-        * Get the index into the agi hash table for the list this inode will
-        * go on.  Make sure the pointer isn't garbage and that this inode
-        * isn't already on the list.
-        */
-       next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]);
-       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;
-       }
-
-       /*
-        * Update the prev pointer in the next inode to point back to this
-        * inode.
-        */
-       error = xfs_iunlink_update_backref(pag, agino, next_agino);
-       if (error == -ENOLINK)
-               error = xfs_iunlink_reload_next(tp, agibp, agino, next_agino);
-       if (error)
-               return error;
-
-       if (next_agino != NULLAGINO) {
-               /*
-                * There is already another inode in the bucket, so point this
-                * inode to the current head of the list.
-                */
-               error = xfs_iunlink_log_inode(tp, ip, pag, next_agino);
-               if (error)
-                       return error;
-               ip->i_next_unlinked = next_agino;
-       }
-
-       /* Point the head of the list to point to this inode. */
-       ip->i_prev_unlinked = NULLAGINO;
-       return xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, agino);
-}
-
-/*
- * This is called when the inode's link count has gone to 0 or we are creating
- * a tmpfile via O_TMPFILE.  The inode @ip must have nlink == 0.
- *
- * We place the on-disk inode on a list in the AGI.  It will be pulled from this
- * list when the inode is freed.
- */
-int
-xfs_iunlink(
-       struct xfs_trans        *tp,
-       struct xfs_inode        *ip)
-{
-       struct xfs_mount        *mp = tp->t_mountp;
-       struct xfs_perag        *pag;
-       struct xfs_buf          *agibp;
-       int                     error;
-
-       ASSERT(VFS_I(ip)->i_nlink == 0);
-       ASSERT(VFS_I(ip)->i_mode != 0);
-       trace_xfs_iunlink(ip);
-
-       pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
-
-       /* Get the agi buffer first.  It ensures lock ordering on the list. */
-       error = xfs_read_agi(pag, tp, 0, &agibp);
-       if (error)
-               goto out;
-
-       error = xfs_iunlink_insert_inode(tp, pag, agibp, ip);
-out:
-       xfs_perag_put(pag);
-       return error;
-}
-
-static int
-xfs_iunlink_remove_inode(
-       struct xfs_trans        *tp,
-       struct xfs_perag        *pag,
-       struct xfs_buf          *agibp,
-       struct xfs_inode        *ip)
-{
-       struct xfs_mount        *mp = tp->t_mountp;
-       struct xfs_agi          *agi = agibp->b_addr;
-       xfs_agino_t             agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
-       xfs_agino_t             head_agino;
-       short                   bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS;
-       int                     error;
-
-       trace_xfs_iunlink_remove(ip);
-
-       /*
-        * Get the index into the agi hash table for the list this inode will
-        * go on.  Make sure the head pointer isn't garbage.
-        */
-       head_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]);
-       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;
-       }
-
-       /*
-        * Set our inode's next_unlinked pointer to NULL and then return
-        * the old pointer value so that we can update whatever was previous
-        * to us in the list to point to whatever was next in the list.
-        */
-       error = xfs_iunlink_log_inode(tp, ip, pag, NULLAGINO);
-       if (error)
-               return error;
-
-       /*
-        * Update the prev pointer in the next inode to point back to previous
-        * inode in the chain.
-        */
-       error = xfs_iunlink_update_backref(pag, ip->i_prev_unlinked,
-                       ip->i_next_unlinked);
-       if (error == -ENOLINK)
-               error = xfs_iunlink_reload_next(tp, agibp, ip->i_prev_unlinked,
-                               ip->i_next_unlinked);
-       if (error)
-               return error;
-
-       if (head_agino != agino) {
-               struct xfs_inode        *prev_ip;
-
-               prev_ip = xfs_iunlink_lookup(pag, ip->i_prev_unlinked);
-               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);
-               prev_ip->i_next_unlinked = ip->i_next_unlinked;
-       } else {
-               /* Point the head of the list to the next unlinked inode. */
-               error = xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index,
-                               ip->i_next_unlinked);
-       }
-
-       ip->i_next_unlinked = NULLAGINO;
-       ip->i_prev_unlinked = 0;
-       return error;
-}
-
-/*
- * Pull the on-disk inode from the AGI unlinked list.
- */
-int
-xfs_iunlink_remove(
-       struct xfs_trans        *tp,
-       struct xfs_perag        *pag,
-       struct xfs_inode        *ip)
-{
-       struct xfs_buf          *agibp;
-       int                     error;
-
-       trace_xfs_iunlink_remove(ip);
-
-       /* Get the agi buffer first.  It ensures lock ordering on the list. */
-       error = xfs_read_agi(pag, tp, 0, &agibp);
-       if (error)
-               return error;
-
-       return xfs_iunlink_remove_inode(tp, pag, agibp, ip);
-}
-
 /*
  * Look up the inode number specified and if it is not already marked XFS_ISTALE
  * mark it stale. We should only find clean inodes in this lookup that aren't
index a905929494bd546ce05935cd26ffe371530b7dac..47d3a11a0e7eff8ad555fbb8a946372230a88431 100644 (file)
@@ -606,10 +606,9 @@ extern struct kmem_cache   *xfs_inode_cache;
 
 bool xfs_inode_needs_inactive(struct xfs_inode *ip);
 
-int xfs_iunlink(struct xfs_trans *tp, struct xfs_inode *ip);
-int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
-               struct xfs_inode *ip);
 struct xfs_inode *xfs_iunlink_lookup(struct xfs_perag *pag, xfs_agino_t agino);
+int xfs_iunlink_reload_next(struct xfs_trans *tp, struct xfs_buf *agibp,
+               xfs_agino_t prev_agino, xfs_agino_t next_agino);
 
 void xfs_end_io(struct work_struct *work);