Commit 135e21a5 authored by Dave Kleikamp's avatar Dave Kleikamp

JFS: Prevent hang in __lock_metapage

Remove the hold_metapage call from txLog to prevent a hang.
While investigating this one, I audited all functions that held
metapage locks and found several error paths that did not release
them correctly.  These are fixed as well.
parent db80df6e
...@@ -1526,6 +1526,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) ...@@ -1526,6 +1526,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
if (n == 4) { if (n == 4) {
jfs_error(bmp->db_ipbmap->i_sb, jfs_error(bmp->db_ipbmap->i_sb,
"dbAllocAG: failed descending stree"); "dbAllocAG: failed descending stree");
release_metapage(mp);
return -EIO; return -EIO;
} }
} }
...@@ -3310,7 +3311,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) ...@@ -3310,7 +3311,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
int i, i0 = TRUE, j, j0 = TRUE, k, n; int i, i0 = TRUE, j, j0 = TRUE, k, n;
s64 newsize; s64 newsize;
s64 p; s64 p;
struct metapage *mp, *l2mp, *l1mp, *l0mp; struct metapage *mp, *l2mp, *l1mp = NULL, *l0mp = NULL;
struct dmapctl *l2dcp, *l1dcp, *l0dcp; struct dmapctl *l2dcp, *l1dcp, *l0dcp;
struct dmap *dp; struct dmap *dp;
s8 *l0leaf, *l1leaf, *l2leaf; s8 *l0leaf, *l1leaf, *l2leaf;
...@@ -3513,6 +3514,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) ...@@ -3513,6 +3514,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
*/ */
*l1leaf = dbInitDmapCtl(l0dcp, 0, ++i); *l1leaf = dbInitDmapCtl(l0dcp, 0, ++i);
write_metapage(l0mp); write_metapage(l0mp);
l0mp = NULL;
if (nblocks) if (nblocks)
l1leaf++; /* continue for next L0 */ l1leaf++; /* continue for next L0 */
...@@ -3536,6 +3538,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) ...@@ -3536,6 +3538,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
*/ */
*l2leaf = dbInitDmapCtl(l1dcp, 1, ++j); *l2leaf = dbInitDmapCtl(l1dcp, 1, ++j);
write_metapage(l1mp); write_metapage(l1mp);
l1mp = NULL;
if (nblocks) if (nblocks)
l2leaf++; /* continue for next L1 */ l2leaf++; /* continue for next L1 */
...@@ -3554,17 +3557,20 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) ...@@ -3554,17 +3557,20 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
jfs_error(ipbmap->i_sb, jfs_error(ipbmap->i_sb,
"dbExtendFS: function has not returned as expected"); "dbExtendFS: function has not returned as expected");
errout:
if (l0mp)
release_metapage(l0mp);
if (l1mp)
release_metapage(l1mp);
release_metapage(l2mp);
return -EIO; return -EIO;
/* /*
* finalize bmap control page * finalize bmap control page
*/ */
finalize: finalize:
return 0; return 0;
errout:
return -EIO;
} }
......
...@@ -1423,8 +1423,10 @@ static int dtSplitPage(tid_t tid, struct inode *ip, struct dtsplit * split, ...@@ -1423,8 +1423,10 @@ static int dtSplitPage(tid_t tid, struct inode *ip, struct dtsplit * split,
*/ */
if (nextbn != 0) { if (nextbn != 0) {
DT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc); DT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc);
if (rc) if (rc) {
discard_metapage(rmp);
return rc; return rc;
}
BT_MARK_DIRTY(mp, ip); BT_MARK_DIRTY(mp, ip);
/* /*
...@@ -2235,8 +2237,10 @@ static int dtDeleteUp(tid_t tid, struct inode *ip, ...@@ -2235,8 +2237,10 @@ static int dtDeleteUp(tid_t tid, struct inode *ip,
pxdlock->index = 1; pxdlock->index = 1;
/* update sibling pointers */ /* update sibling pointers */
if ((rc = dtRelink(tid, ip, fp))) if ((rc = dtRelink(tid, ip, fp))) {
BT_PUTPAGE(fmp);
return rc; return rc;
}
xlen = lengthPXD(&fp->header.self); xlen = lengthPXD(&fp->header.self);
ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen); ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen);
...@@ -2307,8 +2311,10 @@ static int dtDeleteUp(tid_t tid, struct inode *ip, ...@@ -2307,8 +2311,10 @@ static int dtDeleteUp(tid_t tid, struct inode *ip,
pxdlock->index = 1; pxdlock->index = 1;
/* update sibling pointers */ /* update sibling pointers */
if ((rc = dtRelink(tid, ip, p))) if ((rc = dtRelink(tid, ip, p))) {
DT_PUTPAGE(mp);
return rc; return rc;
}
xlen = lengthPXD(&p->header.self); xlen = lengthPXD(&p->header.self);
ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen); ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen);
...@@ -2621,8 +2627,10 @@ static int dtSearchNode(struct inode *ip, s64 lmxaddr, pxd_t * kpxd, ...@@ -2621,8 +2627,10 @@ static int dtSearchNode(struct inode *ip, s64 lmxaddr, pxd_t * kpxd,
/* /*
* descend down to leftmost child page * descend down to leftmost child page
*/ */
if (p->header.flag & BT_LEAF) if (p->header.flag & BT_LEAF) {
DT_PUTPAGE(mp);
return -ESTALE; return -ESTALE;
}
/* get the leftmost entry */ /* get the leftmost entry */
stbl = DT_GETSTBL(p); stbl = DT_GETSTBL(p);
......
...@@ -1546,6 +1546,7 @@ int diAlloc(struct inode *pip, boolean_t dir, struct inode *ip) ...@@ -1546,6 +1546,7 @@ int diAlloc(struct inode *pip, boolean_t dir, struct inode *ip)
0); 0);
if (rem >= INOSPEREXT) { if (rem >= INOSPEREXT) {
IREAD_UNLOCK(ipimap); IREAD_UNLOCK(ipimap);
release_metapage(mp);
AG_UNLOCK(imap, agno); AG_UNLOCK(imap, agno);
jfs_error(ip->i_sb, jfs_error(ip->i_sb,
"diAlloc: can't find free bit " "diAlloc: can't find free bit "
...@@ -1840,6 +1841,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip) ...@@ -1840,6 +1841,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip)
*/ */
if (!iagp->nfreeinos) { if (!iagp->nfreeinos) {
IREAD_UNLOCK(imap->im_ipimap); IREAD_UNLOCK(imap->im_ipimap);
release_metapage(mp);
jfs_error(ip->i_sb, jfs_error(ip->i_sb,
"diAllocIno: nfreeinos = 0, but iag on freelist"); "diAllocIno: nfreeinos = 0, but iag on freelist");
return -EIO; return -EIO;
...@@ -1851,6 +1853,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip) ...@@ -1851,6 +1853,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip)
for (sword = 0;; sword++) { for (sword = 0;; sword++) {
if (sword >= SMAPSZ) { if (sword >= SMAPSZ) {
IREAD_UNLOCK(imap->im_ipimap); IREAD_UNLOCK(imap->im_ipimap);
release_metapage(mp);
jfs_error(ip->i_sb, jfs_error(ip->i_sb,
"diAllocIno: free inode not found in summary map"); "diAllocIno: free inode not found in summary map");
return -EIO; return -EIO;
...@@ -1866,6 +1869,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip) ...@@ -1866,6 +1869,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip)
rem = diFindFree(le32_to_cpu(iagp->inosmap[sword]), 0); rem = diFindFree(le32_to_cpu(iagp->inosmap[sword]), 0);
if (rem >= EXTSPERSUM) { if (rem >= EXTSPERSUM) {
IREAD_UNLOCK(imap->im_ipimap); IREAD_UNLOCK(imap->im_ipimap);
release_metapage(mp);
jfs_error(ip->i_sb, "diAllocIno: no free extent found"); jfs_error(ip->i_sb, "diAllocIno: no free extent found");
return -EIO; return -EIO;
} }
...@@ -1876,6 +1880,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip) ...@@ -1876,6 +1880,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip)
rem = diFindFree(le32_to_cpu(iagp->wmap[extno]), 0); rem = diFindFree(le32_to_cpu(iagp->wmap[extno]), 0);
if (rem >= INOSPEREXT) { if (rem >= INOSPEREXT) {
IREAD_UNLOCK(imap->im_ipimap); IREAD_UNLOCK(imap->im_ipimap);
release_metapage(mp);
jfs_error(ip->i_sb, "diAllocIno: free inode not found"); jfs_error(ip->i_sb, "diAllocIno: free inode not found");
return -EIO; return -EIO;
} }
...@@ -2839,12 +2844,14 @@ diUpdatePMap(struct inode *ipimap, ...@@ -2839,12 +2844,14 @@ diUpdatePMap(struct inode *ipimap,
* and should be free in persistent map; * and should be free in persistent map;
*/ */
if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) { if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) {
release_metapage(mp);
jfs_error(ipimap->i_sb, jfs_error(ipimap->i_sb,
"diUpdatePMap: the inode is not allocated in " "diUpdatePMap: the inode is not allocated in "
"the working map"); "the working map");
return -EIO; return -EIO;
} }
if ((le32_to_cpu(iagp->pmap[extno]) & mask) != 0) { if ((le32_to_cpu(iagp->pmap[extno]) & mask) != 0) {
release_metapage(mp);
jfs_error(ipimap->i_sb, jfs_error(ipimap->i_sb,
"diUpdatePMap: the inode is not free in the " "diUpdatePMap: the inode is not free in the "
"persistent map"); "persistent map");
......
...@@ -1356,9 +1356,6 @@ static int txLog(struct jfs_log * log, struct tblock * tblk, struct commit * cd) ...@@ -1356,9 +1356,6 @@ static int txLog(struct jfs_log * log, struct tblock * tblk, struct commit * cd)
lrd->log.redopage.fileset = cpu_to_le32(JFS_IP(ip)->fileset); lrd->log.redopage.fileset = cpu_to_le32(JFS_IP(ip)->fileset);
lrd->log.redopage.inode = cpu_to_le32(ip->i_ino); lrd->log.redopage.inode = cpu_to_le32(ip->i_ino);
if (tlck->mp)
hold_metapage(tlck->mp, 0);
/* write log record of page from the tlock */ /* write log record of page from the tlock */
switch (tlck->type & tlckTYPE) { switch (tlck->type & tlckTYPE) {
case tlckXTREE: case tlckXTREE:
...@@ -1384,8 +1381,6 @@ static int txLog(struct jfs_log * log, struct tblock * tblk, struct commit * cd) ...@@ -1384,8 +1381,6 @@ static int txLog(struct jfs_log * log, struct tblock * tblk, struct commit * cd)
default: default:
jfs_err("UFO tlock:0x%p", tlck); jfs_err("UFO tlock:0x%p", tlck);
} }
if (tlck->mp)
release_metapage(tlck->mp);
} }
return rc; return rc;
...@@ -1535,6 +1530,7 @@ static int dataLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd, ...@@ -1535,6 +1530,7 @@ static int dataLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
* the last entry, so don't bother logging this * the last entry, so don't bother logging this
*/ */
mp->lid = 0; mp->lid = 0;
hold_metapage(mp, 0);
atomic_dec(&mp->nohomeok); atomic_dec(&mp->nohomeok);
discard_metapage(mp); discard_metapage(mp);
tlck->mp = 0; tlck->mp = 0;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment