Add a close() method to a swapdev.
Add a GEOM based backend. Remove the device/VOP_SPECSTRATEGY() based backend.
This commit is contained in:
parent
20da9c2eaf
commit
dee34ca4fc
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=119591
@ -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);
|
||||
|
||||
/*
|
||||
@ -1999,59 +2004,6 @@ swp_pager_meta_ctl(vm_object_t object, vm_pindex_t pindex, int flags)
|
||||
return (r1);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -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 @@ swapon(struct thread *td, struct swapon_args *uap)
|
||||
}
|
||||
|
||||
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 @@ swapoff(struct thread *td, struct swapoff_args *uap)
|
||||
#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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user