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