From 988890176c7475455b9a90b0a9a81bcb152650c4 Mon Sep 17 00:00:00 2001 From: hannken Date: Mon, 15 Oct 2007 08:12:13 +0000 Subject: [PATCH] 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 --- sys/uvm/uvm_swap.c | 53 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/sys/uvm/uvm_swap.c b/sys/uvm/uvm_swap.c index 6fd38544b3dd..9c8f11408952 100644 --- a/sys/uvm/uvm_swap.c +++ b/sys/uvm/uvm_swap.c @@ -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 -__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 #include #include +#include #include @@ -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",