summaryrefslogtreecommitdiffstats
path: root/sys/vm/swap_pager.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/vm/swap_pager.c')
-rw-r--r--sys/vm/swap_pager.c77
1 files changed, 54 insertions, 23 deletions
diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c
index b8e1014..fd58063 100644
--- a/sys/vm/swap_pager.c
+++ b/sys/vm/swap_pager.c
@@ -1404,8 +1404,6 @@ swap_pager_putpages(vm_object_t object, vm_page_t *m, int count,
blk + j
);
vm_page_dirty(mreq);
- rtvals[i+j] = VM_PAGER_OK;
-
mreq->oflags |= VPO_SWAPINPROG;
bp->b_pages[j] = mreq;
}
@@ -1421,6 +1419,16 @@ swap_pager_putpages(vm_object_t object, vm_page_t *m, int count,
PCPU_ADD(cnt.v_swappgsout, bp->b_npages);
/*
+ * We unconditionally set rtvals[] to VM_PAGER_PEND so that we
+ * can call the async completion routine at the end of a
+ * synchronous I/O operation. Otherwise, our caller would
+ * perform duplicate unbusy and wakeup operations on the page
+ * and object, respectively.
+ */
+ for (j = 0; j < n; j++)
+ rtvals[i + j] = VM_PAGER_PEND;
+
+ /*
* asynchronous
*
* NOTE: b_blkno is destroyed by the call to swapdev_strategy
@@ -1429,10 +1437,6 @@ swap_pager_putpages(vm_object_t object, vm_page_t *m, int count,
bp->b_iodone = swp_pager_async_iodone;
BUF_KERNPROC(bp);
swp_pager_strategy(bp);
-
- for (j = 0; j < n; ++j)
- rtvals[i+j] = VM_PAGER_PEND;
- /* restart outter loop */
continue;
}
@@ -1445,14 +1449,10 @@ swap_pager_putpages(vm_object_t object, vm_page_t *m, int count,
swp_pager_strategy(bp);
/*
- * Wait for the sync I/O to complete, then update rtvals.
- * We just set the rtvals[] to VM_PAGER_PEND so we can call
- * our async completion routine at the end, thus avoiding a
- * double-free.
+ * Wait for the sync I/O to complete.
*/
bwait(bp, PVM, "swwrt");
- for (j = 0; j < n; ++j)
- rtvals[i+j] = VM_PAGER_PEND;
+
/*
* Now that we are through with the bp, we can call the
* normal async completion, which frees everything up.
@@ -2345,8 +2345,8 @@ swapoff_one(struct swdevt *sp, struct ucred *cred)
swap_pager_swapoff(sp);
sp->sw_close(curthread, sp);
- sp->sw_id = NULL;
mtx_lock(&sw_dev_mtx);
+ sp->sw_id = NULL;
TAILQ_REMOVE(&swtailq, sp, sw_list);
nswapdev--;
if (nswapdev == 0) {
@@ -2532,6 +2532,33 @@ swapgeom_close_ev(void *arg, int flags)
g_destroy_consumer(cp);
}
+/*
+ * Add a reference to the g_consumer for an inflight transaction.
+ */
+static void
+swapgeom_acquire(struct g_consumer *cp)
+{
+
+ mtx_assert(&sw_dev_mtx, MA_OWNED);
+ cp->index++;
+}
+
+/*
+ * Remove a reference from the g_consumer. Post a close event if
+ * all referneces go away.
+ */
+static void
+swapgeom_release(struct g_consumer *cp, struct swdevt *sp)
+{
+
+ mtx_assert(&sw_dev_mtx, MA_OWNED);
+ cp->index--;
+ if (cp->index == 0) {
+ if (g_post_event(swapgeom_close_ev, cp, M_NOWAIT, NULL) == 0)
+ sp->sw_id = NULL;
+ }
+}
+
static void
swapgeom_done(struct bio *bp2)
{
@@ -2547,13 +2574,9 @@ swapgeom_done(struct bio *bp2)
bp->b_resid = bp->b_bcount - bp2->bio_completed;
bp->b_error = bp2->bio_error;
bufdone(bp);
+ sp = bp2->bio_caller1;
mtx_lock(&sw_dev_mtx);
- if ((--cp->index) == 0 && cp->private) {
- if (g_post_event(swapgeom_close_ev, cp, M_NOWAIT, NULL) == 0) {
- sp = bp2->bio_caller1;
- sp->sw_id = NULL;
- }
- }
+ swapgeom_release(cp, sp);
mtx_unlock(&sw_dev_mtx);
g_destroy_bio(bp2);
}
@@ -2573,13 +2596,16 @@ swapgeom_strategy(struct buf *bp, struct swdevt *sp)
bufdone(bp);
return;
}
- cp->index++;
+ swapgeom_acquire(cp);
mtx_unlock(&sw_dev_mtx);
if (bp->b_iocmd == BIO_WRITE)
bio = g_new_bio();
else
bio = g_alloc_bio();
if (bio == NULL) {
+ mtx_lock(&sw_dev_mtx);
+ swapgeom_release(cp, sp);
+ mtx_unlock(&sw_dev_mtx);
bp->b_error = ENOMEM;
bp->b_ioflags |= BIO_ERROR;
bufdone(bp);
@@ -2619,7 +2645,12 @@ swapgeom_orphan(struct g_consumer *cp)
break;
}
}
- cp->private = (void *)(uintptr_t)1;
+ /*
+ * Drop reference we were created with. Do directly since we're in a
+ * special context where we don't have to queue the call to
+ * swapgeom_close_ev().
+ */
+ cp->index--;
destroy = ((sp != NULL) && (cp->index == 0));
if (destroy)
sp->sw_id = NULL;
@@ -2680,8 +2711,8 @@ swapongeom_ev(void *arg, int flags)
if (gp == NULL)
gp = g_new_geomf(&g_swap_class, "swap");
cp = g_new_consumer(gp);
- cp->index = 0; /* Number of active I/Os. */
- cp->private = NULL; /* Orphanization flag */
+ cp->index = 1; /* Number of active I/Os, plus one for being active. */
+ cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
g_attach(cp, pp);
/*
* XXX: Everytime you think you can improve the margin for
OpenPOWER on IntegriCloud