summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2003-08-30 16:44:26 +0000
committerphk <phk@FreeBSD.org>2003-08-30 16:44:26 +0000
commit2c584462d57386e8ee2b14a530152a6f24201225 (patch)
tree64801b30b86582da784c7b170f97b1af0b9ac431 /sys
parent0369559168a0dfc9762155240eca21a5e67cf0fa (diff)
downloadFreeBSD-src-2c584462d57386e8ee2b14a530152a6f24201225.zip
FreeBSD-src-2c584462d57386e8ee2b14a530152a6f24201225.tar.gz
Add a close() method to a swapdev.
Add a GEOM based backend. Remove the device/VOP_SPECSTRATEGY() based backend.
Diffstat (limited to 'sys')
-rw-r--r--sys/vm/swap_pager.c392
1 files changed, 254 insertions, 138 deletions
diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c
index a3aa6a2..aa9cdb4 100644
--- a/sys/vm/swap_pager.c
+++ b/sys/vm/swap_pager.c
@@ -107,6 +107,8 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_extern.h>
#include <vm/uma.h>
+#include <geom/geom.h>
+
/*
* SWB_NPAGES must be a power of 2. It may be set to 1, 2, 4, 8, or 16
* pages per allocation. We recommend you stick with the default of 8.
@@ -143,6 +145,7 @@ typedef int32_t swblk_t; /*
struct swdevt;
typedef void sw_strategy_t(struct buf *bp, struct swdevt *sw);
+typedef void sw_close_t(struct thread *td, struct swdevt *sw);
/*
* Swap device table
@@ -152,12 +155,14 @@ struct swdevt {
int sw_nblks;
int sw_used;
udev_t sw_udev;
+ struct vnode *sw_vp;
void *sw_id;
swblk_t sw_first;
swblk_t sw_end;
struct blist *sw_blist;
TAILQ_ENTRY(swdevt) sw_list;
sw_strategy_t *sw_strategy;
+ sw_close_t *sw_close;
};
#define SW_CLOSING 0x04
@@ -258,7 +263,7 @@ SYSCTL_INT(_vm, OID_AUTO, dmmax,
static void swp_sizecheck(void);
static void swp_pager_sync_iodone(struct buf *bp);
static void swp_pager_async_iodone(struct buf *bp);
-static int swapondev(struct thread *, struct vnode *);
+static int swapongeom(struct thread *, struct vnode *);
static int swaponvp(struct thread *, struct vnode *, u_long);
/*
@@ -2000,59 +2005,6 @@ swp_pager_meta_ctl(vm_object_t object, vm_pindex_t pindex, int flags)
}
/*
- * swapdev_strategy:
- *
- * Perform swap strategy interleave device selection.
- *
- * The bp is expected to be locked and *not* B_DONE on call.
- */
-static void
-swapdev_strategy(struct buf *bp, struct swdevt *sp)
-{
- int s;
- struct vnode *vp, *vp2;
-
- bp->b_dev = NODEV;
- bp->b_blkno = ctodb(bp->b_blkno - sp->sw_first);
-
- vp2 = sp->sw_id;
- vhold(vp2);
- s = splvm();
- if (bp->b_iocmd == BIO_WRITE) {
- vp = bp->b_vp;
- if (vp) {
- VI_LOCK(vp);
- vp->v_numoutput--;
- if ((vp->v_iflag & VI_BWAIT) && vp->v_numoutput <= 0) {
- vp->v_iflag &= ~VI_BWAIT;
- wakeup(&vp->v_numoutput);
- }
- VI_UNLOCK(vp);
- }
- VI_LOCK(vp2);
- vp2->v_numoutput++;
- VI_UNLOCK(vp2);
- }
- bp->b_vp = vp2;
- splx(s);
- VOP_STRATEGY(vp2, bp);
- return;
-}
-
-static void
-swapdev_devstrategy(struct buf *bp, struct swdevt *sp)
-{
- struct vnode *vp;
-
- vp = sp->sw_id;
- bp->b_dev = vp->v_rdev;
- bp->b_blkno = ctodb(bp->b_blkno - sp->sw_first);
-
- VOP_SPECSTRATEGY(vp, bp);
- return;
-}
-
-/*
* System call swapon(name) enables swapping on device name,
* which must be in the swdevsw. Return EBUSY
* if already swapping on this device.
@@ -2102,7 +2054,7 @@ swapon(struct thread *td, struct swapon_args *uap)
vp = nd.ni_vp;
if (vn_isdisk(vp, &error)) {
- error = swapondev(td, vp);
+ error = swapongeom(td, vp);
} else if (vp->v_type == VREG &&
(vp->v_mount->mnt_vfc->vfc_flags & VFCF_NETWORK) != 0 &&
(error = VOP_GETATTR(vp, &attr, td->td_ucred, td)) == 0) {
@@ -2124,7 +2076,7 @@ done2:
}
static void
-swaponsomething(void *vp, u_long nblks, sw_strategy_t *strategy, udev_t udev)
+swaponsomething(struct vnode *vp, void *id, u_long nblks, sw_strategy_t *strategy, sw_close_t *close, udev_t udev)
{
struct swdevt *sp;
swblk_t dvbase;
@@ -2164,7 +2116,8 @@ swaponsomething(void *vp, u_long nblks, sw_strategy_t *strategy, udev_t udev)
nblks = dbtoc(nblks);
sp = malloc(sizeof *sp, M_VMPGDATA, M_WAITOK | M_ZERO);
- sp->sw_id = vp;
+ sp->sw_vp = vp;
+ sp->sw_id = id;
sp->sw_udev = udev;
sp->sw_flags = 0;
sp->sw_nblks = nblks;
@@ -2172,6 +2125,7 @@ swaponsomething(void *vp, u_long nblks, sw_strategy_t *strategy, udev_t udev)
sp->sw_first = dvbase;
sp->sw_end = dvbase + nblks;
sp->sw_strategy = strategy;
+ sp->sw_close = close;
sp->sw_blist = blist_create(nblks);
/*
@@ -2188,88 +2142,14 @@ swaponsomething(void *vp, u_long nblks, sw_strategy_t *strategy, udev_t udev)
swap_pager_full = 0;
}
-
-static int
-swapondev(struct thread *td, struct vnode *vp)
-{
- struct swdevt *sp;
- int error;
- off_t mediasize;
- u_long nblks;
-
- mtx_lock(&sw_dev_mtx);
- TAILQ_FOREACH(sp, &swtailq, sw_list) {
- if (sp->sw_id == vp) {
- mtx_unlock(&sw_dev_mtx);
- return (EBUSY);
- }
- }
- mtx_unlock(&sw_dev_mtx);
-
- (void) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
-#ifdef MAC
- error = mac_check_system_swapon(td->td_ucred, vp);
- if (error == 0)
-#endif
- error = VOP_OPEN(vp, FREAD | FWRITE, td->td_ucred, td, -1);
- (void) VOP_UNLOCK(vp, 0, td);
- if (error)
- return (error);
-
- error = VOP_IOCTL(vp, DIOCGMEDIASIZE, (caddr_t)&mediasize,
- FREAD, td->td_ucred, td);
- if (error == 0)
- nblks = mediasize / DEV_BSIZE;
- else {
- (void) VOP_CLOSE(vp, FREAD | FWRITE, td->td_ucred, td);
- return (ENXIO);
- }
-
- /*
- * XXX: We should also check that the sectorsize makes sense
- * XXX: it should be a power of two, no larger than the page size.
- */
-
- swaponsomething(vp, nblks, swapdev_devstrategy, dev2udev(vp->v_rdev));
- return (0);
-}
-
-
-static int
-swaponvp(struct thread *td, struct vnode *vp, u_long nblks)
-{
- struct swdevt *sp;
- int error;
-
- if (nblks == 0)
- return (ENXIO);
- mtx_lock(&sw_dev_mtx);
- TAILQ_FOREACH(sp, &swtailq, sw_list) {
- if (sp->sw_id == vp) {
- mtx_unlock(&sw_dev_mtx);
- return (EBUSY);
- }
- }
- mtx_unlock(&sw_dev_mtx);
-
- (void) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
-#ifdef MAC
- error = mac_check_system_swapon(td->td_ucred, vp);
- if (error == 0)
-#endif
- error = VOP_OPEN(vp, FREAD | FWRITE, td->td_ucred, td, -1);
- (void) VOP_UNLOCK(vp, 0, td);
- if (error)
- return (error);
-
- swaponsomething(vp, nblks, swapdev_strategy, NOUDEV);
- return (0);
-}
-
/*
* SYSCALL: swapoff(devname)
*
* Disable swapping on the given device.
+ *
+ * XXX: Badly designed system call: it should use a device index
+ * rather than filename as specification. We keep sw_vp around
+ * only to make this work.
*/
#ifndef _SYS_SYSPROTO_H_
struct swapoff_args {
@@ -2309,7 +2189,7 @@ swapoff(struct thread *td, struct swapoff_args *uap)
mtx_lock(&sw_dev_mtx);
TAILQ_FOREACH(sp, &swtailq, sw_list) {
- if (sp->sw_id == vp)
+ if (sp->sw_vp == vp)
goto found;
}
mtx_unlock(&sw_dev_mtx);
@@ -2356,8 +2236,7 @@ found:
#endif /* !NO_SWAPPING */
swap_pager_swapoff(sp, &sp->sw_used);
- VOP_CLOSE(vp, FREAD | FWRITE, td->td_ucred, td);
- vrele(vp);
+ sp->sw_close(td, sp);
sp->sw_id = NULL;
mtx_lock(&sw_dev_mtx);
TAILQ_REMOVE(&swtailq, sp, sw_list);
@@ -2463,3 +2342,240 @@ vmspace_swap_count(struct vmspace *vmspace)
}
return (count);
}
+
+/*
+ * GEOM backend
+ *
+ * Swapping onto disk devices.
+ *
+ */
+
+static struct g_class g_swap_class = {
+ .name = "SWAP",
+};
+
+DECLARE_GEOM_CLASS(g_swap_class, g_class);
+
+
+static void
+swapgeom_done(struct bio *bp2)
+{
+ struct buf *bp;
+
+ bp = bp2->bio_caller2;
+ if (bp2->bio_error)
+ bp->b_ioflags |= BIO_ERROR;
+ mtx_lock(&Giant);
+ bufdone(bp);
+ mtx_unlock(&Giant);
+ g_destroy_bio(bp2);
+}
+
+static void
+swapgeom_strategy(struct buf *bp, struct swdevt *sp)
+{
+ struct bio *bio;
+ struct g_consumer *cp;
+
+ cp = sp->sw_id;
+ if (cp == NULL) {
+ bp->b_error = ENXIO;
+ bp->b_ioflags |= BIO_ERROR;
+ bufdone(bp);
+ return;
+ }
+ bio = g_clone_bio(&bp->b_io);
+ bio->bio_caller2 = bp;
+ bio->bio_offset = (bp->b_blkno - sp->sw_first) * PAGE_SIZE;
+ bio->bio_length = bp->b_bcount;
+ bio->bio_done = swapgeom_done;
+ g_io_request(bio, cp);
+ return;
+}
+
+static void
+swapgeom_orphan(struct g_consumer *cp)
+{
+ struct swdevt *sp;
+
+ mtx_lock(&sw_dev_mtx);
+ TAILQ_FOREACH(sp, &swtailq, sw_list)
+ if (sp->sw_id == cp)
+ sp->sw_id = NULL;
+ mtx_unlock(&sw_dev_mtx);
+}
+
+static void
+swapgeom_close_ev(void *arg, int flags)
+{
+ struct g_consumer *cp;
+
+ cp = arg;
+ g_access_rel(cp, -1, -1, -1);
+ g_detach(cp);
+ g_destroy_consumer(cp);
+}
+
+static void
+swapgeom_close(struct thread *td, struct swdevt *sw)
+{
+
+ /* XXX: direct call when Giant untangled */
+ g_waitfor_event(swapgeom_close_ev, sw->sw_id, M_WAITOK, NULL);
+}
+
+
+struct swh0h0 {
+ dev_t dev;
+ struct vnode *vp;
+ int error;
+};
+
+static void
+swapongeom_ev(void *arg, int flags)
+{
+ struct swh0h0 *swh;
+ struct g_provider *pp;
+ struct g_consumer *cp;
+ static struct g_geom *gp;
+ struct swdevt *sp;
+ u_long nblks;
+ int error;
+
+ swh = arg;
+ swh->error = 0;
+ pp = g_dev_getprovider(swh->dev);
+ if (pp == NULL) {
+ swh->error = ENODEV;
+ return;
+ }
+ mtx_lock(&sw_dev_mtx);
+ TAILQ_FOREACH(sp, &swtailq, sw_list) {
+ cp = sp->sw_id;
+ if (cp != NULL && cp->provider == pp) {
+ mtx_unlock(&sw_dev_mtx);
+ swh->error = EBUSY;
+ return;
+ }
+ }
+ mtx_unlock(&sw_dev_mtx);
+ if (gp == NULL) {
+ gp = g_new_geomf(&g_swap_class, "swap", NULL);
+ gp->orphan = swapgeom_orphan;
+ }
+ cp = g_new_consumer(gp);
+ g_attach(cp, pp);
+ error = g_access_rel(cp, 1, 1, 1);
+ if (error) {
+ g_detach(cp);
+ g_destroy_consumer(cp);
+ swh->error = error;
+ return;
+ }
+ nblks = pp->mediasize / DEV_BSIZE;
+ swaponsomething(swh->vp, cp, nblks, swapgeom_strategy,
+ swapgeom_close, dev2udev(swh->dev));
+ swh->error = 0;
+ return;
+}
+
+static int
+swapongeom(struct thread *td, struct vnode *vp)
+{
+ int error;
+ struct swh0h0 swh;
+
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+
+ swh.dev = vp->v_rdev;
+ swh.vp = vp;
+ swh.error = 0;
+ /* XXX: direct call when Giant untangled */
+ error = g_waitfor_event(swapongeom_ev, &swh, M_WAITOK, NULL);
+ if (!error)
+ error = swh.error;
+ VOP_UNLOCK(vp, 0, td);
+ return (error);
+}
+
+/*
+ * VNODE backend
+ *
+ * This is used mainly for network filesystem (read: probably only tested
+ * with NFS) swapfiles.
+ *
+ */
+
+static void
+swapdev_strategy(struct buf *bp, struct swdevt *sp)
+{
+ int s;
+ struct vnode *vp, *vp2;
+
+ bp->b_dev = NODEV;
+ bp->b_blkno = ctodb(bp->b_blkno - sp->sw_first);
+
+ vp2 = sp->sw_id;
+ vhold(vp2);
+ s = splvm();
+ if (bp->b_iocmd == BIO_WRITE) {
+ vp = bp->b_vp;
+ if (vp) {
+ VI_LOCK(vp);
+ vp->v_numoutput--;
+ if ((vp->v_iflag & VI_BWAIT) && vp->v_numoutput <= 0) {
+ vp->v_iflag &= ~VI_BWAIT;
+ wakeup(&vp->v_numoutput);
+ }
+ VI_UNLOCK(vp);
+ }
+ VI_LOCK(vp2);
+ vp2->v_numoutput++;
+ VI_UNLOCK(vp2);
+ }
+ bp->b_vp = vp2;
+ splx(s);
+ VOP_STRATEGY(vp2, bp);
+ return;
+}
+
+static void
+swapdev_close(struct thread *td, struct swdevt *sp)
+{
+
+ VOP_CLOSE(sp->sw_vp, FREAD | FWRITE, td->td_ucred, td);
+ vrele(sp->sw_vp);
+}
+
+
+static int
+swaponvp(struct thread *td, struct vnode *vp, u_long nblks)
+{
+ struct swdevt *sp;
+ int error;
+
+ if (nblks == 0)
+ return (ENXIO);
+ mtx_lock(&sw_dev_mtx);
+ TAILQ_FOREACH(sp, &swtailq, sw_list) {
+ if (sp->sw_id == vp) {
+ mtx_unlock(&sw_dev_mtx);
+ return (EBUSY);
+ }
+ }
+ mtx_unlock(&sw_dev_mtx);
+
+ (void) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+#ifdef MAC
+ error = mac_check_system_swapon(td->td_ucred, vp);
+ if (error == 0)
+#endif
+ error = VOP_OPEN(vp, FREAD | FWRITE, td->td_ucred, td, -1);
+ (void) VOP_UNLOCK(vp, 0, td);
+ if (error)
+ return (error);
+
+ swaponsomething(vp, vp, nblks, swapdev_strategy, swapdev_close,
+ NOUDEV);
+ return (0);
+}
OpenPOWER on IntegriCloud