summaryrefslogtreecommitdiffstats
path: root/sys/ufs/ffs
diff options
context:
space:
mode:
authorjeff <jeff@FreeBSD.org>2008-03-22 09:15:16 +0000
committerjeff <jeff@FreeBSD.org>2008-03-22 09:15:16 +0000
commita9d123c3ab34baa9fe2c8c25bd9acfbfb31b381e (patch)
tree5fedc50643363d96cefce7e3cd6edbdbf2d7fb2b /sys/ufs/ffs
parentb283b3e59a3e18ec4e7cf225a3a9922139733a73 (diff)
downloadFreeBSD-src-a9d123c3ab34baa9fe2c8c25bd9acfbfb31b381e.zip
FreeBSD-src-a9d123c3ab34baa9fe2c8c25bd9acfbfb31b381e.tar.gz
- Complete part of the unfinished bufobj work by consistently using
BO_LOCK/UNLOCK/MTX when manipulating the bufobj. - Create a new lock in the bufobj to lock bufobj fields independently. This leaves the vnode interlock as an 'identity' lock while the bufobj is an io lock. The bufobj lock is ordered before the vnode interlock and also before the mnt ilock. - Exploit this new lock order to simplify softdep_check_suspend(). - A few sync related functions are marked with a new XXX to note that we may not properly interlock against a non-zero bv_cnt when attempting to sync all vnodes on a mountlist. I do not believe this race is important. If I'm wrong this will make these locations easier to find. Reviewed by: kib (earlier diff) Tested by: kris, pho (earlier diff)
Diffstat (limited to 'sys/ufs/ffs')
-rw-r--r--sys/ufs/ffs/ffs_inode.c9
-rw-r--r--sys/ufs/ffs/ffs_rawread.c27
-rw-r--r--sys/ufs/ffs/ffs_softdep.c134
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c7
-rw-r--r--sys/ufs/ffs/ffs_vnops.c26
5 files changed, 99 insertions, 104 deletions
diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c
index a6929f0..0a9bc9d 100644
--- a/sys/ufs/ffs/ffs_inode.c
+++ b/sys/ufs/ffs/ffs_inode.c
@@ -147,6 +147,7 @@ ffs_truncate(vp, length, flags, cred, td)
ufs2_daddr_t bn, lbn, lastblock, lastiblock[NIADDR], indir_lbn[NIADDR];
ufs2_daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
ufs2_daddr_t count, blocksreleased = 0, datablocks;
+ struct bufobj *bo;
struct fs *fs;
struct buf *bp;
struct ufsmount *ump;
@@ -158,6 +159,7 @@ ffs_truncate(vp, length, flags, cred, td)
ip = VTOI(vp);
fs = ip->i_fs;
ump = ip->i_ump;
+ bo = &vp->v_bufobj;
ASSERT_VOP_LOCKED(vp, "ffs_truncate");
@@ -486,13 +488,12 @@ done:
for (i = 0; i < NDADDR; i++)
if (newblks[i] != DIP(ip, i_db[i]))
panic("ffs_truncate2");
- VI_LOCK(vp);
+ BO_LOCK(bo);
if (length == 0 &&
(fs->fs_magic != FS_UFS2_MAGIC || ip->i_din2->di_extsize == 0) &&
- (vp->v_bufobj.bo_dirty.bv_cnt > 0 ||
- vp->v_bufobj.bo_clean.bv_cnt > 0))
+ (bo->bo_dirty.bv_cnt > 0 || bo->bo_clean.bv_cnt > 0))
panic("ffs_truncate3");
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
#endif /* INVARIANTS */
/*
* Put back the real size.
diff --git a/sys/ufs/ffs/ffs_rawread.c b/sys/ufs/ffs/ffs_rawread.c
index f10e432..434c833 100644
--- a/sys/ufs/ffs/ffs_rawread.c
+++ b/sys/ufs/ffs/ffs_rawread.c
@@ -97,21 +97,20 @@ ffs_rawread_setup(void)
static int
ffs_rawread_sync(struct vnode *vp)
{
- int spl;
int error;
int upgraded;
struct bufobj *bo;
struct mount *mp;
/* Check for dirty mmap, pending writes and dirty buffers */
- spl = splbio();
- VI_LOCK(vp);
bo = &vp->v_bufobj;
+ BO_LOCK(bo);
+ VI_LOCK(vp);
if (bo->bo_numoutput > 0 ||
bo->bo_dirty.bv_cnt > 0 ||
(vp->v_iflag & VI_OBJDIRTY) != 0) {
- splx(spl);
VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
if (vn_start_write(vp, &mp, V_NOWAIT) != 0) {
if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE)
@@ -146,16 +145,15 @@ ffs_rawread_sync(struct vnode *vp)
vm_object_page_clean(vp->v_object, 0, 0, OBJPC_SYNC);
VM_OBJECT_UNLOCK(vp->v_object);
}
- VI_LOCK(vp);
- }
+ } else
+ VI_UNLOCK(vp);
/* Wait for pending writes to complete */
- spl = splbio();
+ BO_LOCK(bo);
error = bufobj_wwait(&vp->v_bufobj, 0, 0);
if (error != 0) {
/* XXX: can't happen with a zero timeout ??? */
- splx(spl);
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
if (upgraded != 0)
VOP_LOCK(vp, LK_DOWNGRADE);
vn_finished_write(mp);
@@ -163,27 +161,24 @@ ffs_rawread_sync(struct vnode *vp)
}
/* Flush dirty buffers */
if (bo->bo_dirty.bv_cnt > 0) {
- splx(spl);
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
if ((error = ffs_syncvnode(vp, MNT_WAIT)) != 0) {
if (upgraded != 0)
VOP_LOCK(vp, LK_DOWNGRADE);
vn_finished_write(mp);
return (error);
}
- VI_LOCK(vp);
- spl = splbio();
+ BO_LOCK(bo);
if (bo->bo_numoutput > 0 || bo->bo_dirty.bv_cnt > 0)
panic("ffs_rawread_sync: dirty bufs");
}
- splx(spl);
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
if (upgraded != 0)
VOP_LOCK(vp, LK_DOWNGRADE);
vn_finished_write(mp);
} else {
- splx(spl);
VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
}
return 0;
}
diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c
index 41a6df8..7cb7b40 100644
--- a/sys/ufs/ffs/ffs_softdep.c
+++ b/sys/ufs/ffs/ffs_softdep.c
@@ -357,26 +357,16 @@ softdep_check_suspend(struct mount *mp,
(void) softdep_deps,
(void) softdep_accdeps;
- ASSERT_VI_LOCKED(devvp, "softdep_check_suspend");
bo = &devvp->v_bufobj;
+ ASSERT_BO_LOCKED(bo);
- for (;;) {
- if (!MNT_ITRYLOCK(mp)) {
- VI_UNLOCK(devvp);
- MNT_ILOCK(mp);
- MNT_IUNLOCK(mp);
- VI_LOCK(devvp);
- continue;
- }
- if (mp->mnt_secondary_writes != 0) {
- VI_UNLOCK(devvp);
- msleep(&mp->mnt_secondary_writes,
- MNT_MTX(mp),
- (PUSER - 1) | PDROP, "secwr", 0);
- VI_LOCK(devvp);
- continue;
- }
- break;
+ MNT_ILOCK(mp);
+ while (mp->mnt_secondary_writes != 0) {
+ BO_UNLOCK(bo);
+ msleep(&mp->mnt_secondary_writes, MNT_MTX(mp),
+ (PUSER - 1) | PDROP, "secwr", 0);
+ BO_LOCK(bo);
+ MNT_ILOCK(mp);
}
/*
@@ -391,7 +381,7 @@ softdep_check_suspend(struct mount *mp,
mp->mnt_secondary_writes != 0 ||
secondary_accwrites != mp->mnt_secondary_accwrites)
error = EAGAIN;
- VI_UNLOCK(devvp);
+ BO_UNLOCK(bo);
return (error);
}
@@ -2189,6 +2179,7 @@ softdep_setup_freeblocks(ip, length, flags)
struct freeblks *freeblks;
struct inodedep *inodedep;
struct allocdirect *adp;
+ struct bufobj *bo;
struct vnode *vp;
struct buf *bp;
struct fs *fs;
@@ -2314,27 +2305,28 @@ softdep_setup_freeblocks(ip, length, flags)
* any dependencies.
*/
vp = ITOV(ip);
- VI_LOCK(vp);
+ bo = &vp->v_bufobj;
+ BO_LOCK(bo);
drain_output(vp);
restart:
- TAILQ_FOREACH(bp, &vp->v_bufobj.bo_dirty.bv_hd, b_bobufs) {
+ TAILQ_FOREACH(bp, &bo->bo_dirty.bv_hd, b_bobufs) {
if (((flags & IO_EXT) == 0 && (bp->b_xflags & BX_ALTDATA)) ||
((flags & IO_NORMAL) == 0 &&
(bp->b_xflags & BX_ALTDATA) == 0))
continue;
- if ((bp = getdirtybuf(bp, VI_MTX(vp), MNT_WAIT)) == NULL)
+ if ((bp = getdirtybuf(bp, BO_MTX(bo), MNT_WAIT)) == NULL)
goto restart;
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
ACQUIRE_LOCK(&lk);
(void) inodedep_lookup(mp, ip->i_number, 0, &inodedep);
deallocate_dependencies(bp, inodedep);
FREE_LOCK(&lk);
bp->b_flags |= B_INVAL | B_NOCACHE;
brelse(bp);
- VI_LOCK(vp);
+ BO_LOCK(bo);
goto restart;
}
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(mp, ip->i_number, 0, &inodedep) != 0)
(void) free_inodedep(inodedep);
@@ -5159,13 +5151,15 @@ softdep_fsync_mountdev(vp)
{
struct buf *bp, *nbp;
struct worklist *wk;
+ struct bufobj *bo;
if (!vn_isdisk(vp, NULL))
panic("softdep_fsync_mountdev: vnode not a disk");
+ bo = &vp->v_bufobj;
restart:
+ BO_LOCK(bo);
ACQUIRE_LOCK(&lk);
- VI_LOCK(vp);
- TAILQ_FOREACH_SAFE(bp, &vp->v_bufobj.bo_dirty.bv_hd, b_bobufs, nbp) {
+ TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
/*
* If it is already scheduled, skip to the next buffer.
*/
@@ -5184,15 +5178,15 @@ restart:
BUF_UNLOCK(bp);
continue;
}
- VI_UNLOCK(vp);
FREE_LOCK(&lk);
+ BO_UNLOCK(bo);
bremfree(bp);
(void) bawrite(bp);
goto restart;
}
FREE_LOCK(&lk);
drain_output(vp);
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
}
/*
@@ -5209,6 +5203,7 @@ softdep_sync_metadata(struct vnode *vp)
struct allocindir *aip;
struct buf *bp, *nbp;
struct worklist *wk;
+ struct bufobj *bo;
int i, error, waitfor;
if (!DOINGSOFTDEP(vp))
@@ -5240,20 +5235,21 @@ softdep_sync_metadata(struct vnode *vp)
* resolved. Thus the second pass is expected to end quickly.
*/
waitfor = MNT_NOWAIT;
+ bo = &vp->v_bufobj;
top:
/*
* We must wait for any I/O in progress to finish so that
* all potential buffers on the dirty list will be visible.
*/
- VI_LOCK(vp);
+ BO_LOCK(bo);
drain_output(vp);
- while ((bp = TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd)) != NULL) {
- bp = getdirtybuf(bp, VI_MTX(vp), MNT_WAIT);
+ while ((bp = TAILQ_FIRST(&bo->bo_dirty.bv_hd)) != NULL) {
+ bp = getdirtybuf(bp, BO_MTX(bo), MNT_WAIT);
if (bp)
break;
}
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
if (bp == NULL)
return (0);
loop:
@@ -5405,13 +5401,13 @@ loop:
return (error);
}
FREE_LOCK(&lk);
- VI_LOCK(vp);
+ BO_LOCK(bo);
while ((nbp = TAILQ_NEXT(bp, b_bobufs)) != NULL) {
- nbp = getdirtybuf(nbp, VI_MTX(vp), MNT_WAIT);
+ nbp = getdirtybuf(nbp, BO_MTX(bo), MNT_WAIT);
if (nbp)
break;
}
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
BUF_NOREC(bp);
bawrite(bp);
if (nbp != NULL) {
@@ -5435,9 +5431,9 @@ loop:
* We must wait for any I/O in progress to finish so that
* all potential buffers on the dirty list will be visible.
*/
- VI_LOCK(vp);
+ BO_LOCK(bo);
drain_output(vp);
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
return (0);
}
@@ -5544,6 +5540,7 @@ flush_pagedep_deps(pvp, mp, diraddhdp)
struct ufsmount *ump;
struct diradd *dap;
struct vnode *vp;
+ struct bufobj *bo;
int error = 0;
struct buf *bp;
ino_t inum;
@@ -5590,7 +5587,8 @@ flush_pagedep_deps(pvp, mp, diraddhdp)
vput(vp);
break;
}
- VI_LOCK(vp);
+ bo = &vp->v_bufobj;
+ BO_LOCK(bo);
drain_output(vp);
/*
* If first block is still dirty with a D_MKDIR
@@ -5598,15 +5596,15 @@ flush_pagedep_deps(pvp, mp, diraddhdp)
*/
for (;;) {
error = 0;
- bp = gbincore(&vp->v_bufobj, 0);
+ bp = gbincore(bo, 0);
if (bp == NULL)
break; /* First block not present */
error = BUF_LOCK(bp,
LK_EXCLUSIVE |
LK_SLEEPFAIL |
LK_INTERLOCK,
- VI_MTX(vp));
- VI_LOCK(vp);
+ BO_MTX(bo));
+ BO_LOCK(bo);
if (error == ENOLCK)
continue; /* Slept, retry */
if (error != 0)
@@ -5628,14 +5626,14 @@ flush_pagedep_deps(pvp, mp, diraddhdp)
* must write buffer to stable
* storage.
*/
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
bremfree(bp);
error = bwrite(bp);
- VI_LOCK(vp);
+ BO_LOCK(bo);
}
break;
}
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
vput(vp);
if (error != 0)
break; /* Flushing of first block failed */
@@ -5904,6 +5902,7 @@ clear_remove(td)
static int next = 0;
struct mount *mp;
struct vnode *vp;
+ struct bufobj *bo;
int error, cnt;
ino_t ino;
@@ -5929,9 +5928,10 @@ clear_remove(td)
}
if ((error = ffs_syncvnode(vp, MNT_NOWAIT)))
softdep_error("clear_remove: fsync", error);
- VI_LOCK(vp);
+ bo = &vp->v_bufobj;
+ BO_LOCK(bo);
drain_output(vp);
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
vput(vp);
vn_finished_write(mp);
ACQUIRE_LOCK(&lk);
@@ -6004,9 +6004,9 @@ clear_inodedeps(td)
} else {
if ((error = ffs_syncvnode(vp, MNT_NOWAIT)))
softdep_error("clear_inodedeps: fsync2", error);
- VI_LOCK(vp);
+ BO_LOCK(&vp->v_bufobj);
drain_output(vp);
- VI_UNLOCK(vp);
+ BO_UNLOCK(&vp->v_bufobj);
}
vput(vp);
vn_finished_write(mp);
@@ -6154,7 +6154,7 @@ getdirtybuf(bp, mtx, waitfor)
*/
#ifdef DEBUG_VFS_LOCKS
if (bp->b_vp->v_type != VCHR)
- ASSERT_VI_LOCKED(bp->b_vp, "getdirtybuf");
+ ASSERT_BO_LOCKED(bp->b_bufobj);
#endif
bp->b_vflags |= BV_BKGRDWAIT;
msleep(&bp->b_xflags, mtx, PRIBIO, "getbuf", 0);
@@ -6187,33 +6187,26 @@ softdep_check_suspend(struct mount *mp,
struct ufsmount *ump;
int error;
- ASSERT_VI_LOCKED(devvp, "softdep_check_suspend");
ump = VFSTOUFS(mp);
bo = &devvp->v_bufobj;
+ ASSERT_BO_LOCKED(bo);
for (;;) {
if (!TRY_ACQUIRE_LOCK(&lk)) {
- VI_UNLOCK(devvp);
+ BO_UNLOCK(bo);
ACQUIRE_LOCK(&lk);
FREE_LOCK(&lk);
- VI_LOCK(devvp);
- continue;
- }
- if (!MNT_ITRYLOCK(mp)) {
- FREE_LOCK(&lk);
- VI_UNLOCK(devvp);
- MNT_ILOCK(mp);
- MNT_IUNLOCK(mp);
- VI_LOCK(devvp);
+ BO_LOCK(bo);
continue;
}
+ MNT_ILOCK(mp);
if (mp->mnt_secondary_writes != 0) {
FREE_LOCK(&lk);
- VI_UNLOCK(devvp);
+ BO_UNLOCK(bo);
msleep(&mp->mnt_secondary_writes,
MNT_MTX(mp),
(PUSER - 1) | PDROP, "secwr", 0);
- VI_LOCK(devvp);
+ BO_LOCK(bo);
continue;
}
break;
@@ -6236,7 +6229,7 @@ softdep_check_suspend(struct mount *mp,
secondary_accwrites != mp->mnt_secondary_accwrites)
error = EAGAIN;
FREE_LOCK(&lk);
- VI_UNLOCK(devvp);
+ BO_UNLOCK(bo);
return (error);
}
@@ -6270,13 +6263,16 @@ static void
drain_output(vp)
struct vnode *vp;
{
+ struct bufobj *bo;
+
+ bo = &vp->v_bufobj;
ASSERT_VOP_LOCKED(vp, "drain_output");
- ASSERT_VI_LOCKED(vp, "drain_output");
+ ASSERT_BO_LOCKED(bo);
- while (vp->v_bufobj.bo_numoutput) {
- vp->v_bufobj.bo_flag |= BO_WWAIT;
- msleep((caddr_t)&vp->v_bufobj.bo_numoutput,
- VI_MTX(vp), PRIBIO + 1, "drainvp", 0);
+ while (bo->bo_numoutput) {
+ bo->bo_flag |= BO_WWAIT;
+ msleep((caddr_t)&bo->bo_numoutput,
+ BO_MTX(bo), PRIBIO + 1, "drainvp", 0);
}
}
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 6a1dce3..cc13410 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -1267,11 +1267,12 @@ loop:
qsync(mp);
#endif
devvp = ump->um_devvp;
- VI_LOCK(devvp);
bo = &devvp->v_bufobj;
+ BO_LOCK(bo);
if (waitfor != MNT_LAZY &&
(bo->bo_numoutput > 0 || bo->bo_dirty.bv_cnt > 0)) {
- vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK);
+ BO_UNLOCK(bo);
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
if ((error = VOP_FSYNC(devvp, waitfor, td)) != 0)
allerror = error;
VOP_UNLOCK(devvp, 0);
@@ -1292,7 +1293,7 @@ loop:
MNT_IUNLOCK(mp);
suspended = 1;
} else
- VI_UNLOCK(devvp);
+ BO_UNLOCK(bo);
/*
* Write back modified superblock.
*/
diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
index 7968662..dca262c 100644
--- a/sys/ufs/ffs/ffs_vnops.c
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -195,6 +195,7 @@ int
ffs_syncvnode(struct vnode *vp, int waitfor)
{
struct inode *ip = VTOI(vp);
+ struct bufobj *bo;
struct buf *bp;
struct buf *nbp;
int s, error, wait, passes, skipmeta;
@@ -202,6 +203,7 @@ ffs_syncvnode(struct vnode *vp, int waitfor)
wait = (waitfor == MNT_WAIT);
lbn = lblkno(ip->i_fs, (ip->i_size + ip->i_fs->fs_bsize - 1));
+ bo = &vp->v_bufobj;
/*
* Flush all dirty buffers associated with a vnode.
@@ -211,11 +213,11 @@ ffs_syncvnode(struct vnode *vp, int waitfor)
if (wait)
skipmeta = 1;
s = splbio();
- VI_LOCK(vp);
+ BO_LOCK(bo);
loop:
- TAILQ_FOREACH(bp, &vp->v_bufobj.bo_dirty.bv_hd, b_bobufs)
+ TAILQ_FOREACH(bp, &bo->bo_dirty.bv_hd, b_bobufs)
bp->b_vflags &= ~BV_SCANNED;
- TAILQ_FOREACH_SAFE(bp, &vp->v_bufobj.bo_dirty.bv_hd, b_bobufs, nbp) {
+ TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
/*
* Reasons to skip this buffer: it has already been considered
* on this pass, this pass is the first time through on a
@@ -231,13 +233,13 @@ loop:
continue;
if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL))
continue;
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
if (!wait && !LIST_EMPTY(&bp->b_dep) &&
(bp->b_flags & B_DEFERRED) == 0 &&
buf_countdeps(bp, 0)) {
bp->b_flags |= B_DEFERRED;
BUF_UNLOCK(bp);
- VI_LOCK(vp);
+ BO_LOCK(bo);
continue;
}
if ((bp->b_flags & B_DELWRI) == 0)
@@ -286,8 +288,8 @@ loop:
* Since we may have slept during the I/O, we need
* to start from a known point.
*/
- VI_LOCK(vp);
- nbp = TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd);
+ BO_LOCK(bo);
+ nbp = TAILQ_FIRST(&bo->bo_dirty.bv_hd);
}
/*
* If we were asked to do this synchronously, then go back for
@@ -299,8 +301,8 @@ loop:
}
if (wait) {
- bufobj_wwait(&vp->v_bufobj, 3, 0);
- VI_UNLOCK(vp);
+ bufobj_wwait(bo, 3, 0);
+ BO_UNLOCK(bo);
/*
* Ensure that any filesystem metatdata associated
@@ -311,8 +313,8 @@ loop:
return (error);
s = splbio();
- VI_LOCK(vp);
- if (vp->v_bufobj.bo_dirty.bv_cnt > 0) {
+ BO_LOCK(bo);
+ if (bo->bo_dirty.bv_cnt > 0) {
/*
* Block devices associated with filesystems may
* have new I/O requests posted for them even if
@@ -331,7 +333,7 @@ loop:
#endif
}
}
- VI_UNLOCK(vp);
+ BO_UNLOCK(bo);
splx(s);
return (ffs_update(vp, wait));
}
OpenPOWER on IntegriCloud