]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/vm/vm_swap.c
This commit was generated by cvs2svn to compensate for changes in r80062,
[FreeBSD/FreeBSD.git] / sys / vm / vm_swap.c
1 /*
2  * Copyright (c) 1982, 1986, 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *      @(#)vm_swap.c   8.5 (Berkeley) 2/17/94
34  * $FreeBSD$
35  */
36
37 #include "opt_swap.h"
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/sysproto.h>
42 #include <sys/bio.h>
43 #include <sys/buf.h>
44 #include <sys/proc.h>
45 #include <sys/namei.h>
46 #include <sys/dmap.h>           /* XXX */
47 #include <sys/vnode.h>
48 #include <sys/fcntl.h>
49 #include <sys/blist.h>
50 #include <sys/kernel.h>
51 #include <sys/lock.h>
52 #include <sys/conf.h>
53 #include <sys/stat.h>
54 #include <sys/sysctl.h>
55 #include <vm/vm.h>
56 #include <vm/vm_extern.h>
57 #include <vm/vm_zone.h>
58 #include <vm/vm_param.h>
59 #include <vm/swap_pager.h>
60
61 /*
62  * Indirect driver for multi-controller paging.
63  */
64
65 #ifndef NSWAPDEV
66 #define NSWAPDEV        4
67 #endif
68 static struct swdevt should_be_malloced[NSWAPDEV];
69 struct swdevt *swdevt = should_be_malloced;
70 static int nswap;               /* first block after the interleaved devs */
71 int nswdev = NSWAPDEV;
72 int vm_swap_size;
73
74 static int swapdev_strategy __P((struct vop_strategy_args *ap));
75 struct vnode *swapdev_vp;
76
77 /*
78  *      swapdev_strategy:
79  *
80  *      VOP_STRATEGY() for swapdev_vp.
81  *      Perform swap strategy interleave device selection.
82  *
83  *      The bp is expected to be locked and *not* B_DONE on call.
84  */
85
86 static int
87 swapdev_strategy(ap)
88         struct vop_strategy_args /* {
89                 struct vnode *a_vp;
90                 struct buf *a_bp;
91         } */ *ap;
92 {
93         int s, sz, off, seg, index;
94         struct swdevt *sp;
95         struct vnode *vp;
96         struct buf *bp;
97
98         bp = ap->a_bp;
99         sz = howmany(bp->b_bcount, PAGE_SIZE);
100
101         /*
102          * Convert interleaved swap into per-device swap.  Note that
103          * the block size is left in PAGE_SIZE'd chunks (for the newswap)
104          * here.
105          */
106         if (nswdev > 1) {
107                 off = bp->b_blkno % dmmax;
108                 if (off + sz > dmmax) {
109                         bp->b_error = EINVAL;
110                         bp->b_ioflags |= BIO_ERROR;
111                         bufdone(bp);
112                         return 0;
113                 }
114                 seg = bp->b_blkno / dmmax;
115                 index = seg % nswdev;
116                 seg /= nswdev;
117                 bp->b_blkno = seg * dmmax + off;
118         } else {
119                 index = 0;
120         }
121         sp = &swdevt[index];
122         if (bp->b_blkno + sz > sp->sw_nblks) {
123                 bp->b_error = EINVAL;
124                 bp->b_ioflags |= BIO_ERROR;
125                 bufdone(bp);
126                 return 0;
127         }
128         bp->b_dev = sp->sw_device;
129         if (sp->sw_vp == NULL) {
130                 bp->b_error = ENODEV;
131                 bp->b_ioflags |= BIO_ERROR;
132                 bufdone(bp);
133                 return 0;
134         }
135
136         /*
137          * Convert from PAGE_SIZE'd to DEV_BSIZE'd chunks for the actual I/O
138          */
139         bp->b_blkno = ctodb(bp->b_blkno);
140
141         vhold(sp->sw_vp);
142         s = splvm();
143         if (bp->b_iocmd == BIO_WRITE) {
144                 vp = bp->b_vp;
145                 if (vp) {
146                         vp->v_numoutput--;
147                         if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
148                                 vp->v_flag &= ~VBWAIT;
149                                 wakeup(&vp->v_numoutput);
150                         }
151                 }
152                 sp->sw_vp->v_numoutput++;
153         }
154         pbreassignbuf(bp, sp->sw_vp);
155         splx(s);
156         BUF_STRATEGY(bp);
157         return 0;
158 }
159
160 /*
161  * Create a special vnode op vector for swapdev_vp - we only use
162  * VOP_STRATEGY(), everything else returns an error.
163  */
164 vop_t **swapdev_vnodeop_p;
165 static struct vnodeopv_entry_desc swapdev_vnodeop_entries[] = {  
166         { &vop_default_desc,            (vop_t *) vop_defaultop },
167         { &vop_strategy_desc,           (vop_t *) swapdev_strategy },
168         { NULL, NULL }
169 };
170 static struct vnodeopv_desc swapdev_vnodeop_opv_desc =
171         { &swapdev_vnodeop_p, swapdev_vnodeop_entries };
172
173 VNODEOP_SET(swapdev_vnodeop_opv_desc);
174
175 /*
176  * System call swapon(name) enables swapping on device name,
177  * which must be in the swdevsw.  Return EBUSY
178  * if already swapping on this device.
179  */
180 #ifndef _SYS_SYSPROTO_H_
181 struct swapon_args {
182         char *name;
183 };
184 #endif
185
186 /* ARGSUSED */
187 int
188 swapon(p, uap)
189         struct proc *p;
190         struct swapon_args *uap;
191 {
192         struct vnode *vp;
193         struct nameidata nd;
194         int error;
195
196         error = suser(p);
197         if (error)
198                 return (error);
199
200         /*
201          * Swap metadata may not fit in the KVM if we have physical
202          * memory of >1GB.
203          */
204         if (swap_zone == NULL)
205                 return (ENOMEM);
206
207         NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->name, p);
208         error = namei(&nd);
209         if (error)
210                 return (error);
211
212         NDFREE(&nd, NDF_ONLY_PNBUF);
213         vp = nd.ni_vp;
214
215         vn_isdisk(vp, &error);
216
217         if (!error)
218                 error = swaponvp(p, vp, vp->v_rdev, 0);
219
220         if (error)
221                 vrele(vp);
222
223         return (error);
224 }
225
226 /*
227  * Swfree(index) frees the index'th portion of the swap map.
228  * Each of the nswdev devices provides 1/nswdev'th of the swap
229  * space, which is laid out with blocks of dmmax pages circularly
230  * among the devices.
231  *
232  * The new swap code uses page-sized blocks.  The old swap code used
233  * DEV_BSIZE'd chunks.
234  *
235  * XXX locking when multiple swapon's run in parallel
236  */
237 int
238 swaponvp(p, vp, dev, nblks)
239         struct proc *p;
240         struct vnode *vp;
241         dev_t dev;
242         u_long nblks;
243 {
244         int index;
245         struct swdevt *sp;
246         swblk_t vsbase;
247         long blk;
248         swblk_t dvbase;
249         int error;
250         u_long aligned_nblks;
251
252         if (!swapdev_vp) {
253                 error = getnewvnode(VT_NON, NULL, swapdev_vnodeop_p,
254                     &swapdev_vp);
255                 if (error)
256                         panic("Cannot get vnode for swapdev");
257                 swapdev_vp->v_type = VNON;      /* Untyped */
258         }
259
260         ASSERT_VOP_UNLOCKED(vp, "swaponvp");
261         for (sp = swdevt, index = 0 ; index < nswdev; index++, sp++) {
262                 if (sp->sw_vp == vp)
263                         return EBUSY;
264                 if (!sp->sw_vp)
265                         goto found;
266
267         }
268         return EINVAL;
269     found:
270         (void) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
271         error = VOP_OPEN(vp, FREAD | FWRITE, p->p_ucred, p);
272         (void) VOP_UNLOCK(vp, 0, p);
273         if (error)
274                 return (error);
275
276         if (nblks == 0 && dev != NODEV && (devsw(dev)->d_psize == 0 ||
277             (nblks = (*devsw(dev)->d_psize) (dev)) == -1)) {
278                 (void) VOP_CLOSE(vp, FREAD | FWRITE, p->p_ucred, p);
279                 return (ENXIO);
280         }
281         if (nblks == 0) {
282                 (void) VOP_CLOSE(vp, FREAD | FWRITE, p->p_ucred, p);
283                 return (ENXIO);
284         }
285
286         /*
287          * If we go beyond this, we get overflows in the radix
288          * tree bitmap code.
289          */
290         if (nblks > 0x40000000 / BLIST_META_RADIX / nswdev) {
291                 printf("exceeded maximum of %d blocks per swap unit\n",
292                         0x40000000 / BLIST_META_RADIX / nswdev);
293                 (void) VOP_CLOSE(vp, FREAD | FWRITE, p->p_ucred, p);
294                 return (ENXIO);
295         }
296         /*
297          * nblks is in DEV_BSIZE'd chunks, convert to PAGE_SIZE'd chunks.
298          * First chop nblks off to page-align it, then convert.
299          * 
300          * sw->sw_nblks is in page-sized chunks now too.
301          */
302         nblks &= ~(ctodb(1) - 1);
303         nblks = dbtoc(nblks);
304
305         sp->sw_vp = vp;
306         sp->sw_dev = dev2udev(dev);
307         sp->sw_device = dev;
308         sp->sw_flags |= SW_FREED;
309         sp->sw_nblks = nblks;
310         sp->sw_used = 0;
311
312         /*
313          * nblks, nswap, and dmmax are PAGE_SIZE'd parameters now, not
314          * DEV_BSIZE'd.   aligned_nblks is used to calculate the
315          * size of the swap bitmap, taking into account the stripe size.
316          */
317         aligned_nblks = (nblks + (dmmax - 1)) & ~(u_long)(dmmax - 1);
318
319         if (aligned_nblks * nswdev > nswap)
320                 nswap = aligned_nblks * nswdev;
321
322         if (swapblist == NULL)
323                 swapblist = blist_create(nswap);
324         else
325                 blist_resize(&swapblist, nswap, 0);
326
327         for (dvbase = dmmax; dvbase < nblks; dvbase += dmmax) {
328                 blk = min(nblks - dvbase, dmmax);
329                 vsbase = index * dmmax + dvbase * nswdev;
330                 blist_free(swapblist, vsbase, blk);
331                 vm_swap_size += blk;
332         }
333
334         return (0);
335 }
336
337 static int
338 sysctl_vm_swap_info(SYSCTL_HANDLER_ARGS)
339 {
340         int     *name = (int *)arg1;
341         int     error, i, n;
342         struct xswdev xs;
343         struct swdevt *sp;
344
345         if (arg2 != 1) /* name length */
346                 return (EINVAL);
347
348         for (sp = swdevt, i = 0, n = 0 ; i < nswdev; i++, sp++) {
349                 if (sp->sw_vp) {
350                         if (n == *name) {
351                                 xs.xsw_version = XSWDEV_VERSION;
352                                 xs.xsw_dev = sp->sw_dev;
353                                 xs.xsw_flags = sp->sw_flags;
354                                 xs.xsw_nblks = sp->sw_nblks;
355                                 xs.xsw_used = sp->sw_used;
356
357                                 error = SYSCTL_OUT(req, &xs, sizeof(xs));
358                                 return (error);
359                         }
360                         n++;
361                 }
362
363         }
364         return (ENOENT);
365 }
366
367 SYSCTL_NODE(_vm, OID_AUTO, swap_info, CTLFLAG_RD, sysctl_vm_swap_info,
368     "Swap statistics by device");