]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/vm/vm_swap.c
This commit was generated by cvs2svn to compensate for changes in r85587,
[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 /* 
187  * MPSAFE
188  */
189 /* ARGSUSED */
190 int
191 swapon(td, uap)
192         struct thread *td;
193         struct swapon_args *uap;
194 {
195         struct vattr attr;
196         struct vnode *vp;
197         struct nameidata nd;
198         int error;
199
200         mtx_lock(&Giant);
201         error = suser_td(td);
202         if (error)
203                 goto done2;
204
205         /*
206          * Swap metadata may not fit in the KVM if we have physical
207          * memory of >1GB.
208          */
209         if (swap_zone == NULL) {
210                 error = ENOMEM;
211                 goto done2;
212         }
213
214         NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->name, td);
215         error = namei(&nd);
216         if (error)
217                 goto done2;
218
219         NDFREE(&nd, NDF_ONLY_PNBUF);
220         vp = nd.ni_vp;
221
222         if (vn_isdisk(vp, &error))
223                 error = swaponvp(td, vp, vp->v_rdev, 0);
224         else if (vp->v_type == VREG && vp->v_tag == VT_NFS &&
225             (error = VOP_GETATTR(vp, &attr, td->td_proc->p_ucred, td)) == 0) {
226                 /*
227                  * Allow direct swapping to NFS regular files in the same
228                  * way that nfs_mountroot() sets up diskless swapping.
229                  */
230                 error = swaponvp(td, vp, NODEV, attr.va_size / DEV_BSIZE);
231         }
232
233         if (error)
234                 vrele(vp);
235 done2:
236         mtx_unlock(&Giant);
237         return (error);
238 }
239
240 /*
241  * Swfree(index) frees the index'th portion of the swap map.
242  * Each of the nswdev devices provides 1/nswdev'th of the swap
243  * space, which is laid out with blocks of dmmax pages circularly
244  * among the devices.
245  *
246  * The new swap code uses page-sized blocks.  The old swap code used
247  * DEV_BSIZE'd chunks.
248  *
249  * XXX locking when multiple swapon's run in parallel
250  */
251 int
252 swaponvp(td, vp, dev, nblks)
253         struct thread *td;
254         struct vnode *vp;
255         dev_t dev;
256         u_long nblks;
257 {
258         int index;
259         struct swdevt *sp;
260         swblk_t vsbase;
261         long blk;
262         swblk_t dvbase;
263         int error;
264         u_long aligned_nblks;
265         struct proc *p = td->td_proc;
266
267         if (!swapdev_vp) {
268                 error = getnewvnode(VT_NON, NULL, swapdev_vnodeop_p,
269                     &swapdev_vp);
270                 if (error)
271                         panic("Cannot get vnode for swapdev");
272                 swapdev_vp->v_type = VNON;      /* Untyped */
273         }
274
275         ASSERT_VOP_UNLOCKED(vp, "swaponvp");
276         for (sp = swdevt, index = 0 ; index < nswdev; index++, sp++) {
277                 if (sp->sw_vp == vp)
278                         return EBUSY;
279                 if (!sp->sw_vp)
280                         goto found;
281
282         }
283         return EINVAL;
284     found:
285         (void) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
286         error = VOP_OPEN(vp, FREAD | FWRITE, p->p_ucred, td);
287         (void) VOP_UNLOCK(vp, 0, td);
288         if (error)
289                 return (error);
290
291         if (nblks == 0 && dev != NODEV && (devsw(dev)->d_psize == 0 ||
292             (nblks = (*devsw(dev)->d_psize) (dev)) == -1)) {
293                 (void) VOP_CLOSE(vp, FREAD | FWRITE, p->p_ucred, td);
294                 return (ENXIO);
295         }
296         if (nblks == 0) {
297                 (void) VOP_CLOSE(vp, FREAD | FWRITE, p->p_ucred, td);
298                 return (ENXIO);
299         }
300
301         /*
302          * If we go beyond this, we get overflows in the radix
303          * tree bitmap code.
304          */
305         if (nblks > 0x40000000 / BLIST_META_RADIX / nswdev) {
306                 printf("exceeded maximum of %d blocks per swap unit\n",
307                         0x40000000 / BLIST_META_RADIX / nswdev);
308                 (void) VOP_CLOSE(vp, FREAD | FWRITE, p->p_ucred, td);
309                 return (ENXIO);
310         }
311         /*
312          * nblks is in DEV_BSIZE'd chunks, convert to PAGE_SIZE'd chunks.
313          * First chop nblks off to page-align it, then convert.
314          * 
315          * sw->sw_nblks is in page-sized chunks now too.
316          */
317         nblks &= ~(ctodb(1) - 1);
318         nblks = dbtoc(nblks);
319
320         sp->sw_vp = vp;
321         sp->sw_dev = dev2udev(dev);
322         sp->sw_device = dev;
323         sp->sw_flags |= SW_FREED;
324         sp->sw_nblks = nblks;
325         sp->sw_used = 0;
326
327         /*
328          * nblks, nswap, and dmmax are PAGE_SIZE'd parameters now, not
329          * DEV_BSIZE'd.   aligned_nblks is used to calculate the
330          * size of the swap bitmap, taking into account the stripe size.
331          */
332         aligned_nblks = (nblks + (dmmax - 1)) & ~(u_long)(dmmax - 1);
333
334         if (aligned_nblks * nswdev > nswap)
335                 nswap = aligned_nblks * nswdev;
336
337         if (swapblist == NULL)
338                 swapblist = blist_create(nswap);
339         else
340                 blist_resize(&swapblist, nswap, 0);
341
342         for (dvbase = dmmax; dvbase < nblks; dvbase += dmmax) {
343                 blk = min(nblks - dvbase, dmmax);
344                 vsbase = index * dmmax + dvbase * nswdev;
345                 blist_free(swapblist, vsbase, blk);
346                 vm_swap_size += blk;
347         }
348
349         return (0);
350 }
351
352 static int
353 sysctl_vm_swap_info(SYSCTL_HANDLER_ARGS)
354 {
355         int     *name = (int *)arg1;
356         int     error, i, n;
357         struct xswdev xs;
358         struct swdevt *sp;
359
360         if (arg2 != 1) /* name length */
361                 return (EINVAL);
362
363         for (sp = swdevt, i = 0, n = 0 ; i < nswdev; i++, sp++) {
364                 if (sp->sw_vp) {
365                         if (n == *name) {
366                                 xs.xsw_version = XSWDEV_VERSION;
367                                 xs.xsw_dev = sp->sw_dev;
368                                 xs.xsw_flags = sp->sw_flags;
369                                 xs.xsw_nblks = sp->sw_nblks;
370                                 xs.xsw_used = sp->sw_used;
371
372                                 error = SYSCTL_OUT(req, &xs, sizeof(xs));
373                                 return (error);
374                         }
375                         n++;
376                 }
377
378         }
379         return (ENOENT);
380 }
381
382 SYSCTL_NODE(_vm, OID_AUTO, swap_info, CTLFLAG_RD, sysctl_vm_swap_info,
383     "Swap statistics by device");