]> err.no Git - linux-2.6/commitdiff
[XFS] Fixes a bug in the quota code when allocating a new dquot record
authorTim Shimmin <tes@sgi.com>
Sun, 4 Sep 2005 22:29:01 +0000 (08:29 +1000)
committerNathan Scott <nathans@sgi.com>
Sun, 4 Sep 2005 22:29:01 +0000 (08:29 +1000)
which can cause an extent hole to be filled and a free extent to be
processed. In this case, we make a few mistakes: forget to pass back the
transaction, forget to put a hold on the buffer and forget to add the buf
to the new transaction.

SGI-PV: 940366
SGI-Modid: xfs-linux:xfs-kern:23594a

Signed-off-by: Tim Shimmin <tes@sgi.com>
Signed-off-by: Nathan Scott <nathans@sgi.com>
fs/xfs/quota/xfs_dquot.c
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_buf.c

index 46ce1e3ce1d6c04c90b93ec46d86d2e4f64651fc..e2e8d35fa4d0361517bdb1d76f7c08c6de512f20 100644 (file)
@@ -421,7 +421,7 @@ xfs_qm_init_dquot_blk(
  */
 STATIC int
 xfs_qm_dqalloc(
-       xfs_trans_t     *tp,
+       xfs_trans_t     **tpp,
        xfs_mount_t     *mp,
        xfs_dquot_t     *dqp,
        xfs_inode_t     *quotip,
@@ -433,6 +433,7 @@ xfs_qm_dqalloc(
        xfs_bmbt_irec_t map;
        int             nmaps, error, committed;
        xfs_buf_t       *bp;
+       xfs_trans_t     *tp = *tpp;
 
        ASSERT(tp != NULL);
        xfs_dqtrace_entry(dqp, "DQALLOC");
@@ -492,10 +493,32 @@ xfs_qm_dqalloc(
        xfs_qm_init_dquot_blk(tp, mp, INT_GET(dqp->q_core.d_id, ARCH_CONVERT),
                              dqp->dq_flags & XFS_DQ_ALLTYPES, bp);
 
-       if ((error = xfs_bmap_finish(&tp, &flist, firstblock, &committed))) {
+       /*
+        * xfs_bmap_finish() may commit the current transaction and
+        * start a second transaction if the freelist is not empty.
+        *
+        * Since we still want to modify this buffer, we need to
+        * ensure that the buffer is not released on commit of
+        * the first transaction and ensure the buffer is added to the
+        * second transaction.
+        *
+        * If there is only one transaction then don't stop the buffer
+        * from being released when it commits later on.
+        */
+
+       xfs_trans_bhold(tp, bp);
+
+       if ((error = xfs_bmap_finish(tpp, &flist, firstblock, &committed))) {
                goto error1;
        }
 
+       if (committed) {
+               tp = *tpp;
+               xfs_trans_bjoin(tp, bp);
+       } else {
+               xfs_trans_bhold_release(tp, bp);
+       }
+
        *O_bpp = bp;
        return 0;
 
@@ -514,7 +537,7 @@ xfs_qm_dqalloc(
  */
 STATIC int
 xfs_qm_dqtobp(
-       xfs_trans_t             *tp,
+       xfs_trans_t             **tpp,
        xfs_dquot_t             *dqp,
        xfs_disk_dquot_t        **O_ddpp,
        xfs_buf_t               **O_bpp,
@@ -528,6 +551,7 @@ xfs_qm_dqtobp(
        xfs_disk_dquot_t *ddq;
        xfs_dqid_t      id;
        boolean_t       newdquot;
+       xfs_trans_t     *tp = (tpp ? *tpp : NULL);
 
        mp = dqp->q_mount;
        id = INT_GET(dqp->q_core.d_id, ARCH_CONVERT);
@@ -579,9 +603,10 @@ xfs_qm_dqtobp(
                                return (ENOENT);
 
                        ASSERT(tp);
-                       if ((error = xfs_qm_dqalloc(tp, mp, dqp, quotip,
+                       if ((error = xfs_qm_dqalloc(tpp, mp, dqp, quotip,
                                                dqp->q_fileoffset, &bp)))
                                return (error);
+                       tp = *tpp;
                        newdquot = B_TRUE;
                } else {
                        /*
@@ -645,7 +670,7 @@ xfs_qm_dqtobp(
 /* ARGSUSED */
 STATIC int
 xfs_qm_dqread(
-       xfs_trans_t     *tp,
+       xfs_trans_t     **tpp,
        xfs_dqid_t      id,
        xfs_dquot_t     *dqp,   /* dquot to get filled in */
        uint            flags)
@@ -653,15 +678,19 @@ xfs_qm_dqread(
        xfs_disk_dquot_t *ddqp;
        xfs_buf_t        *bp;
        int              error;
+       xfs_trans_t      *tp;
+
+       ASSERT(tpp);
 
        /*
         * get a pointer to the on-disk dquot and the buffer containing it
         * dqp already knows its own type (GROUP/USER).
         */
        xfs_dqtrace_entry(dqp, "DQREAD");
-       if ((error = xfs_qm_dqtobp(tp, dqp, &ddqp, &bp, flags))) {
+       if ((error = xfs_qm_dqtobp(tpp, dqp, &ddqp, &bp, flags))) {
                return (error);
        }
+       tp = *tpp;
 
        /* copy everything from disk dquot to the incore dquot */
        memcpy(&dqp->q_core, ddqp, sizeof(xfs_disk_dquot_t));
@@ -740,7 +769,7 @@ xfs_qm_idtodq(
         * Read it from disk; xfs_dqread() takes care of
         * all the necessary initialization of dquot's fields (locks, etc)
         */
-       if ((error = xfs_qm_dqread(tp, id, dqp, flags))) {
+       if ((error = xfs_qm_dqread(&tp, id, dqp, flags))) {
                /*
                 * This can happen if quotas got turned off (ESRCH),
                 * or if the dquot didn't exist on disk and we ask to
index 9ee5eeee80260befc187a4e8da468ea1591a5d7b..a263aec8b3a6532b8dd8b887cc6f073bf4348f42 100644 (file)
@@ -999,6 +999,7 @@ struct xfs_buf      *xfs_trans_getsb(xfs_trans_t *, struct xfs_mount *, int);
 void           xfs_trans_brelse(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_bjoin(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_bhold(xfs_trans_t *, struct xfs_buf *);
+void           xfs_trans_bhold_release(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_binval(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *);
index 144da7a85466c2e85fbffe66ff1f15e08c0873ec..e733293dd7f488f24a8d7bedf5dc7b3cf1428085 100644 (file)
@@ -713,6 +713,29 @@ xfs_trans_bhold(xfs_trans_t        *tp,
        xfs_buf_item_trace("BHOLD", bip);
 }
 
+/*
+ * Cancel the previous buffer hold request made on this buffer
+ * for this transaction.
+ */
+void
+xfs_trans_bhold_release(xfs_trans_t    *tp,
+                       xfs_buf_t       *bp)
+{
+       xfs_buf_log_item_t      *bip;
+
+       ASSERT(XFS_BUF_ISBUSY(bp));
+       ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+       ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+
+       bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+       ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+       ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
+       ASSERT(atomic_read(&bip->bli_refcount) > 0);
+       ASSERT(bip->bli_flags & XFS_BLI_HOLD);
+       bip->bli_flags &= ~XFS_BLI_HOLD;
+       xfs_buf_item_trace("BHOLD RELEASE", bip);
+}
+
 /*
  * This is called to mark bytes first through last inclusive of the given
  * buffer as needing to be logged when the transaction is committed.