When swapping to a regular file use a workqueue to signal I/O completion.
VOP_STRATEGY() no longer gets called from interrupt context via biodone() -> sw_reg_iodone() -> sw_reg_start(). Removes a deadlock condition reported in PR 37109. Ok: YAMAMOTO Takashi <yamt@netbsd.org>
This commit is contained in:
parent
1d55b1afc4
commit
988890176c
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: uvm_swap.c,v 1.129 2007/07/29 13:31:18 ad Exp $ */
|
||||
/* $NetBSD: uvm_swap.c,v 1.130 2007/10/15 08:12:13 hannken Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1995, 1996, 1997 Matthew R. Green
|
||||
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: uvm_swap.c,v 1.129 2007/07/29 13:31:18 ad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: uvm_swap.c,v 1.130 2007/10/15 08:12:13 hannken Exp $");
|
||||
|
||||
#include "fs_nfs.h"
|
||||
#include "opt_uvmhist.h"
|
||||
|
@ -60,6 +60,7 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_swap.c,v 1.129 2007/07/29 13:31:18 ad Exp $");
|
|||
#include <sys/swap.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/workqueue.h>
|
||||
|
||||
#include <uvm/uvm.h>
|
||||
|
||||
|
@ -220,6 +221,10 @@ static struct swap_priority swap_priority;
|
|||
/* locks */
|
||||
static krwlock_t swap_syscall_lock;
|
||||
|
||||
/* workqueue and use counter for swap to regular files */
|
||||
static int sw_reg_count = 0;
|
||||
static struct workqueue *sw_reg_workqueue;
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
|
@ -236,7 +241,8 @@ static int swap_off(struct lwp *, struct swapdev *);
|
|||
static void uvm_swap_stats_locked(int, struct swapent *, int, register_t *);
|
||||
|
||||
static void sw_reg_strategy(struct swapdev *, struct buf *, int);
|
||||
static void sw_reg_iodone(struct buf *);
|
||||
static void sw_reg_biodone(struct buf *);
|
||||
static void sw_reg_iodone(struct work *wk, void *dummy);
|
||||
static void sw_reg_start(struct swapdev *);
|
||||
|
||||
static int uvm_swap_io(struct vm_page **, int, int, int);
|
||||
|
@ -958,6 +964,18 @@ swap_on(struct lwp *l, struct swapdev *sdp)
|
|||
result = vmem_alloc(swapmap, npages, VM_BESTFIT | VM_SLEEP);
|
||||
if (result == 0)
|
||||
panic("swapdrum_add");
|
||||
/*
|
||||
* If this is the first regular swap create the workqueue.
|
||||
* => Protected by swap_syscall_lock.
|
||||
*/
|
||||
if (vp->v_type != VBLK) {
|
||||
if (sw_reg_count++ == 0) {
|
||||
KASSERT(sw_reg_workqueue == NULL);
|
||||
if (workqueue_create(&sw_reg_workqueue, "swapiod",
|
||||
sw_reg_iodone, NULL, PRIBIO, IPL_BIO, 0) != 0)
|
||||
panic("swap_add: workqueue_create failed");
|
||||
}
|
||||
}
|
||||
|
||||
sdp->swd_drumoffset = (int)result;
|
||||
sdp->swd_drumsize = npages;
|
||||
|
@ -1028,6 +1046,19 @@ swap_off(struct lwp *l, struct swapdev *sdp)
|
|||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is the last regular swap destroy the workqueue.
|
||||
* => Protected by swap_syscall_lock.
|
||||
*/
|
||||
if (sdp->swd_vp->v_type != VBLK) {
|
||||
KASSERT(sw_reg_count > 0);
|
||||
KASSERT(sw_reg_workqueue != NULL);
|
||||
if (--sw_reg_count == 0) {
|
||||
workqueue_destroy(sw_reg_workqueue);
|
||||
sw_reg_workqueue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* done with the vnode.
|
||||
* drop our ref on the vnode before calling VOP_CLOSE()
|
||||
|
@ -1287,7 +1318,7 @@ sw_reg_strategy(struct swapdev *sdp, struct buf *bp, int bn)
|
|||
nbp->vb_buf.b_lblkno = 0;
|
||||
nbp->vb_buf.b_blkno = nbn + btodb(off);
|
||||
nbp->vb_buf.b_rawblkno = nbp->vb_buf.b_blkno;
|
||||
nbp->vb_buf.b_iodone = sw_reg_iodone;
|
||||
nbp->vb_buf.b_iodone = sw_reg_biodone;
|
||||
nbp->vb_buf.b_vp = vp;
|
||||
if (vp->v_type == VBLK) {
|
||||
nbp->vb_buf.b_dev = vp->v_rdev;
|
||||
|
@ -1364,19 +1395,29 @@ sw_reg_start(struct swapdev *sdp)
|
|||
sdp->swd_flags &= ~SWF_BUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* sw_reg_biodone: one of our i/o's has completed
|
||||
*/
|
||||
static void
|
||||
sw_reg_biodone(struct buf *bp)
|
||||
{
|
||||
workqueue_enqueue(sw_reg_workqueue, &bp->b_work, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* sw_reg_iodone: one of our i/o's has completed and needs post-i/o cleanup
|
||||
*
|
||||
* => note that we can recover the vndbuf struct by casting the buf ptr
|
||||
*/
|
||||
static void
|
||||
sw_reg_iodone(struct buf *bp)
|
||||
sw_reg_iodone(struct work *wk, void *dummy)
|
||||
{
|
||||
struct vndbuf *vbp = (struct vndbuf *) bp;
|
||||
struct vndbuf *vbp = (void *)wk;
|
||||
struct vndxfer *vnx = vbp->vb_xfer;
|
||||
struct buf *pbp = vnx->vx_bp; /* parent buffer */
|
||||
struct swapdev *sdp = vnx->vx_sdp;
|
||||
int s, resid, error;
|
||||
KASSERT(&vbp->vb_buf.b_work == wk);
|
||||
UVMHIST_FUNC("sw_reg_iodone"); UVMHIST_CALLED(pdhist);
|
||||
|
||||
UVMHIST_LOG(pdhist, " vbp=%p vp=%p blkno=%x addr=%p",
|
||||
|
|
Loading…
Reference in New Issue