FreeBSD/Linux Kernel Cross Reference
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: src/sys/vm/vm_swap.c,v 1.40.2.2 1999/09/05 08:24:41 peter Exp $
35 */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/sysproto.h>
40 #include <sys/buf.h>
41 #include <sys/conf.h>
42 #ifdef DEVFS
43 #include <sys/devfsext.h>
44 #endif
45 #include <sys/proc.h>
46 #include <sys/namei.h>
47 #include <sys/dmap.h> /* XXX */
48 #include <sys/vnode.h>
49 #include <sys/file.h>
50 #include <sys/rlist.h>
51 #include <sys/kernel.h>
52 #include <vm/vm.h>
53 #include <vm/vm_param.h>
54 #include <vm/vm_extern.h>
55 #include <vm/swap_pager.h>
56
57 #include <miscfs/specfs/specdev.h>
58
59 static void swstrategy __P((struct buf *));
60
61 #define CDEV_MAJOR 4
62 #define BDEV_MAJOR 26
63 extern struct cdevsw sw_cdevsw ;
64
65 static struct bdevsw sw_bdevsw =
66 { noopen, noclose, swstrategy, noioc, /*1*/
67 nodump, nopsize, 0, "sw", &sw_cdevsw, -1 };
68
69 static struct cdevsw sw_cdevsw =
70 { nullopen, nullclose, rawread, rawwrite, /*4*/
71 noioc, nostop, noreset, nodevtotty,/* swap */
72 noselect, nommap, swstrategy, "sw",
73 &sw_bdevsw, -1 };
74
75
76 /*
77 * Swapdev is a fake device implemented
78 * in sw.c used only internally to get to swstrategy.
79 * It cannot be provided to the users, because the
80 * swstrategy routine munches the b_dev and b_blkno entries
81 * before calling the appropriate driver. This would horribly
82 * confuse, e.g. the hashing routines. Instead, /dev/drum is
83 * provided as a character (raw) device.
84 */
85 dev_t swapdev = makedev(BDEV_MAJOR, 0);
86
87 /*
88 * Indirect driver for multi-controller paging.
89 */
90
91 #ifndef NSWAPDEV
92 #define NSWAPDEV 4
93 #endif
94 static struct swdevt should_be_malloced[NSWAPDEV];
95 static struct swdevt *swdevt = should_be_malloced;
96 struct vnode *swapdev_vp;
97 /* XXX swapinfo(8) needs this one I belive */
98 int nswap; /* first block after the interleaved devs */
99 static int nswdev = NSWAPDEV;
100 int vm_swap_size;
101
102 static void
103 swstrategy(bp)
104 register struct buf *bp;
105 {
106 int sz, off, seg, index;
107 register struct swdevt *sp;
108 struct vnode *vp;
109
110 sz = howmany(bp->b_bcount, DEV_BSIZE);
111 if (nswdev > 1) {
112 off = bp->b_blkno % dmmax;
113 if (off + sz > dmmax) {
114 bp->b_error = EINVAL;
115 bp->b_flags |= B_ERROR;
116 biodone(bp);
117 return;
118 }
119 seg = bp->b_blkno / dmmax;
120 index = seg % nswdev;
121 seg /= nswdev;
122 bp->b_blkno = seg * dmmax + off;
123 } else
124 index = 0;
125 sp = &swdevt[index];
126 if (bp->b_blkno + sz > sp->sw_nblks) {
127 bp->b_error = EINVAL;
128 bp->b_flags |= B_ERROR;
129 biodone(bp);
130 return;
131 }
132 bp->b_dev = sp->sw_dev;
133 if (sp->sw_vp == NULL) {
134 bp->b_error = ENODEV;
135 bp->b_flags |= B_ERROR;
136 biodone(bp);
137 return;
138 }
139 VHOLD(sp->sw_vp);
140 if ((bp->b_flags & B_READ) == 0) {
141 vp = bp->b_vp;
142 if (vp) {
143 vp->v_numoutput--;
144 if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
145 vp->v_flag &= ~VBWAIT;
146 wakeup(&vp->v_numoutput);
147 }
148 }
149 sp->sw_vp->v_numoutput++;
150 }
151 if (bp->b_vp != NULL)
152 pbrelvp(bp);
153 bp->b_vp = sp->sw_vp;
154 VOP_STRATEGY(bp);
155 }
156
157 /*
158 * System call swapon(name) enables swapping on device name,
159 * which must be in the swdevsw. Return EBUSY
160 * if already swapping on this device.
161 */
162 #ifndef _SYS_SYSPROTO_H_
163 struct swapon_args {
164 char *name;
165 };
166 #endif
167
168 /* ARGSUSED */
169 int
170 swapon(p, uap, retval)
171 struct proc *p;
172 struct swapon_args *uap;
173 int *retval;
174 {
175 register struct vnode *vp;
176 dev_t dev;
177 struct nameidata nd;
178 int error;
179
180 error = suser(p->p_ucred, &p->p_acflag);
181 if (error)
182 return (error);
183
184 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->name, p);
185 error = namei(&nd);
186 if (error)
187 return (error);
188
189 vp = nd.ni_vp;
190
191 switch (vp->v_type) {
192 case VBLK:
193 dev = (dev_t) vp->v_rdev;
194 if (major(dev) >= nblkdev) {
195 error = ENXIO;
196 break;
197 }
198 error = swaponvp(p, vp, dev, 0);
199 break;
200 case VCHR:
201 /*
202 * For now, we disallow swapping to regular files.
203 * It requires logical->physcal block translation
204 * support in the swap pager before it will work.
205 */
206 error = ENOTBLK;
207 break;
208 #if 0
209 error = VOP_GETATTR(vp, &attr, p->p_ucred, p);
210 if (!error)
211 error = swaponvp(p, vp, NODEV, attr.va_size / DEV_BSIZE);
212 break;
213 #endif
214 default:
215 error = EINVAL;
216 break;
217 }
218
219 if (error)
220 vrele(vp);
221
222 return (error);
223 }
224
225 /*
226 * Swfree(index) frees the index'th portion of the swap map.
227 * Each of the nswdev devices provides 1/nswdev'th of the swap
228 * space, which is laid out with blocks of dmmax pages circularly
229 * among the devices.
230 */
231 int
232 swaponvp(p, vp, dev, nblks)
233 struct proc *p;
234 struct vnode *vp;
235 dev_t dev;
236 u_long nblks;
237 {
238 int index;
239 register struct swdevt *sp;
240 register swblk_t vsbase;
241 register long blk;
242 swblk_t dvbase;
243 int error;
244
245 for (sp = swdevt, index = 0 ; index < nswdev; index++, sp++) {
246 if (sp->sw_vp == vp)
247 return EBUSY;
248 if (!sp->sw_vp)
249 goto found;
250
251 }
252 return EINVAL;
253 found:
254 if (dev != NODEV && (major(dev) >= nblkdev))
255 return (ENXIO);
256
257 error = VOP_OPEN(vp, FREAD | FWRITE, p->p_ucred, p);
258 if (error)
259 return (error);
260
261 if (nblks == 0 && (bdevsw[major(dev)]->d_psize == 0 ||
262 (nblks = (*bdevsw[major(dev)]->d_psize) (dev)) == -1)) {
263 (void) VOP_CLOSE(vp, FREAD | FWRITE, p->p_ucred, p);
264 return (ENXIO);
265 }
266 if (nblks == 0) {
267 (void) VOP_CLOSE(vp, FREAD | FWRITE, p->p_ucred, p);
268 return (ENXIO);
269 }
270 sp->sw_vp = vp;
271 sp->sw_dev = dev;
272 sp->sw_flags |= SW_FREED;
273 sp->sw_nblks = nblks;
274
275 if (nblks * nswdev > nswap)
276 nswap = (nblks+1) * nswdev;
277
278 for (dvbase = dmmax; dvbase < nblks; dvbase += dmmax) {
279 blk = min(nblks - dvbase,dmmax);
280 vsbase = index * dmmax + dvbase * nswdev;
281 rlist_free(&swaplist, vsbase, vsbase + blk - 1);
282 vm_swap_size += blk;
283 }
284
285 if (!swapdev_vp) {
286 struct vnode *vp;
287 struct vnode *nvp;
288
289 error = getnewvnode(VT_NON, (struct mount *) 0,
290 spec_vnodeop_p, &nvp);
291 if (error)
292 panic("Cannot get vnode for swapdev");
293 vp = nvp;
294 vp->v_type = VBLK;
295 if ((nvp = checkalias(vp, swapdev,
296 (struct mount *) 0))) {
297 vput(vp);
298 vp = nvp;
299 }
300 swapdev_vp = vp;
301 }
302 return (0);
303 }
304
305 static sw_devsw_installed = 0;
306 #ifdef DEVFS
307 static void *drum_devfs_token;
308 #endif
309
310 static void sw_drvinit(void *unused)
311 {
312 dev_t dev;
313
314 if( ! sw_devsw_installed ) {
315 dev = makedev(CDEV_MAJOR,0);
316 cdevsw_add(&dev,&sw_cdevsw,NULL);
317 dev = makedev(BDEV_MAJOR,0);
318 bdevsw_add(&dev,&sw_bdevsw,NULL);
319 sw_devsw_installed = 1;
320 #ifdef DEVFS
321 drum_devfs_token = devfs_add_devswf(&sw_cdevsw, 0, DV_CHR,
322 UID_ROOT, GID_KMEM, 0640,
323 "drum");
324 #endif
325 }
326 }
327
328 SYSINIT(swdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,sw_drvinit,NULL)
329
Cache object: ee3fed23ea62ad2050813fffe5c5a06e
|