]> git.sur5r.net Git - openldap/commitdiff
Fix loose pages in mdb_freelist_save().
authorHallvard Furuseth <h.b.furuseth@usit.uio.no>
Sat, 16 Aug 2014 22:29:12 +0000 (00:29 +0200)
committerHallvard Furuseth <h.b.furuseth@usit.uio.no>
Sat, 16 Aug 2014 22:29:12 +0000 (00:29 +0200)
Leaving them in dirty_list caused breakage.  Instead merge them
into me_pghead at the end, when no more pages will be allocated.

libraries/liblmdb/mdb.c

index 4e238880445f1eb901f110ebccfde4a71c5d0b3e..47a230bbfd095a2aef3018af36db849112938857 100644 (file)
@@ -745,7 +745,7 @@ typedef struct MDB_page {
        /** The number of overflow pages needed to store the given size. */
 #define OVPAGES(size, psize)   ((PAGEHDRSZ-1 + (size)) / (psize) + 1)
 
-       /** Link in #MDB_txn.%mt_loose_pages list */
+       /** Link in #MDB_txn.%mt_loose_pgs list */
 #define NEXT_LOOSE_PAGE(p)             (*(MDB_page **)((p) + 2))
 
        /** Header for a single key/data pair within a page.
@@ -950,6 +950,8 @@ struct MDB_txn {
         *      in this transaction, linked through #NEXT_LOOSE_PAGE(page).
         */
        MDB_page        *mt_loose_pgs;
+       /* #Number of loose pages (#mt_loose_pgs) */
+       int                     mt_loose_count;
        /** The sorted list of dirty pages we temporarily wrote to disk
         *      because the dirty list was full. page numbers in here are
         *      shifted left by 1, deleted slots have the LSB set.
@@ -1661,6 +1663,7 @@ mdb_page_loose(MDB_cursor *mc, MDB_page *mp)
                        mp->mp_pgno));
                NEXT_LOOSE_PAGE(mp) = mc->mc_txn->mt_loose_pgs;
                mc->mc_txn->mt_loose_pgs = mp;
+               mc->mc_txn->mt_loose_count++;
                mp->mp_flags |= P_LOOSE;
        } else {
                int rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, pgno);
@@ -1950,6 +1953,7 @@ mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp)
        if (num == 1 && txn->mt_loose_pgs) {
                np = txn->mt_loose_pgs;
                txn->mt_loose_pgs = NEXT_LOOSE_PAGE(np);
+               txn->mt_loose_count--;
                DPRINTF(("db %d use loose page %"Z"u", DDBI(mc),
                                np->mp_pgno));
                *mp = np;
@@ -2823,30 +2827,17 @@ mdb_freelist_save(MDB_txn *txn)
                        return rc;
        }
 
-       /* Dispose of loose pages. Usually they will have all
-        * been used up by the time we get here.
-        */
-       if (txn->mt_loose_pgs) {
+       if (!env->me_pghead) {
+               /* Put loose page numbers in mt_free_pgs, since
+                * we may be unable to return them to me_pghead.
+                */
                MDB_page *mp = txn->mt_loose_pgs;
-               /* Just return them to freeDB */
-               if (env->me_pghead) {
-                       int i, j;
-                       mop = env->me_pghead;
-                       for (; mp; mp = NEXT_LOOSE_PAGE(mp)) {
-                               pgno_t pg = mp->mp_pgno;
-                               j = mop[0] + 1;
-                               for (i = mop[0]; i && mop[i] < pg; i--)
-                                       mop[j--] = mop[i];
-                               mop[j] = pg;
-                               mop[0] += 1;
-                       }
-               } else {
-               /* Oh well, they were wasted. Put on freelist */
-                       for (; mp; mp = NEXT_LOOSE_PAGE(mp)) {
-                               mdb_midl_append(&txn->mt_free_pgs, mp->mp_pgno);
-                       }
-               }
+               if ((rc = mdb_midl_need(&txn->mt_free_pgs, txn->mt_loose_count)) != 0)
+                       return rc;
+               for (; mp; mp = NEXT_LOOSE_PAGE(mp))
+                       mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno);
                txn->mt_loose_pgs = NULL;
+               txn->mt_loose_count = 0;
        }
 
        /* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */
@@ -2910,7 +2901,7 @@ mdb_freelist_save(MDB_txn *txn)
                }
 
                mop = env->me_pghead;
-               mop_len = mop ? mop[0] : 0;
+               mop_len = (mop ? mop[0] : 0) + txn->mt_loose_count;
 
                /* Reserve records for me_pghead[]. Split it if multi-page,
                 * to avoid searching freeDB for a page range. Use keys in
@@ -2950,6 +2941,28 @@ mdb_freelist_save(MDB_txn *txn)
                total_room += head_room;
        }
 
+       /* Return loose page numbers to me_pghead, though usually none are
+        * left at this point.  The pages themselves remain in dirty_list.
+        */
+       if (txn->mt_loose_pgs) {
+               MDB_page *mp = txn->mt_loose_pgs;
+               unsigned count = txn->mt_loose_count;
+               MDB_IDL loose;
+               /* Room for loose pages + temp IDL with same */
+               if ((rc = mdb_midl_need(&env->me_pghead, 2*count+1)) != 0)
+                       return rc;
+               mop = env->me_pghead;
+               loose = mop + MDB_IDL_ALLOCLEN(mop) - count;
+               for (count = 0; mp; mp = NEXT_LOOSE_PAGE(mp))
+                       loose[ ++count ] = mp->mp_pgno;
+               loose[0] = count;
+               mdb_midl_sort(loose);
+               mdb_midl_xmerge(mop, loose);
+               txn->mt_loose_pgs = NULL;
+               txn->mt_loose_count = 0;
+               mop_len = mop[0];
+       }
+
        /* Fill in the reserved me_pghead records */
        rc = MDB_SUCCESS;
        if (mop_len) {
@@ -3264,6 +3277,7 @@ mdb_txn_commit(MDB_txn *txn)
                for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(lp))
                        ;
                *lp = txn->mt_loose_pgs;
+               parent->mt_loose_count += txn->mt_loose_count;
 
                parent->mt_child = NULL;
                mdb_midl_free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pghead);
@@ -3326,6 +3340,10 @@ mdb_txn_commit(MDB_txn *txn)
                (rc = mdb_env_write_meta(txn)))
                goto fail;
 
+       /* Free P_LOOSE pages left behind in dirty_list */
+       if (!(env->me_flags & MDB_WRITEMAP))
+               mdb_dlist_free(txn);
+
 done:
        env->me_pglast = 0;
        env->me_txn = NULL;