xref: /csrg-svn/sys/vm/vm_swap.c (revision 39880)
1 /*
2  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  *	@(#)vm_swap.c	7.9 (Berkeley) 01/04/90
18  */
19 
20 #include "param.h"
21 #include "systm.h"
22 #include "buf.h"
23 #include "conf.h"
24 #include "user.h"
25 #include "vnode.h"
26 #include "map.h"
27 #include "file.h"
28 
29 /*
30  * Indirect driver for multi-controller paging.
31  */
32 swstrategy(bp)
33 	register struct buf *bp;
34 {
35 	int sz, off, seg, index;
36 	register struct swdevt *sp;
37 
38 #ifdef GENERIC
39 	/*
40 	 * A mini-root gets copied into the front of the swap
41 	 * and we run over top of the swap area just long
42 	 * enough for us to do a mkfs and restor of the real
43 	 * root (sure beats rewriting standalone restor).
44 	 */
45 #define	MINIROOTSIZE	4096
46 	if (rootdev == dumpdev)
47 		bp->b_blkno += MINIROOTSIZE;
48 #endif
49 	sz = howmany(bp->b_bcount, DEV_BSIZE);
50 	if (bp->b_blkno+sz > nswap) {
51 		bp->b_flags |= B_ERROR;
52 		biodone(bp);
53 		return;
54 	}
55 	if (nswdev > 1) {
56 		off = bp->b_blkno % dmmax;
57 		if (off+sz > dmmax) {
58 			bp->b_flags |= B_ERROR;
59 			biodone(bp);
60 			return;
61 		}
62 		seg = bp->b_blkno / dmmax;
63 		index = seg % nswdev;
64 		seg /= nswdev;
65 		bp->b_blkno = seg*dmmax + off;
66 	} else
67 		index = 0;
68 	sp = &swdevt[index];
69 	bp->b_dev = sp->sw_dev;
70 	if (bp->b_vp == NULL || bp->b_dev == 0)
71 		panic("swstrategy");
72 	if (sp->sw_vp == NULL) {
73 		bp->b_error |= B_ERROR;
74 		biodone(bp);
75 		return;
76 	}
77 	VHOLD(sp->sw_vp);
78 	if ((bp->b_flags & B_READ) == 0) {
79 		bp->b_vp->v_numoutput--;
80 		if ((bp->b_vp->v_flag & VBWAIT) && bp->b_vp->v_numoutput <= 0) {
81 			bp->b_vp->v_flag &= ~VBWAIT;
82 			wakeup((caddr_t)&bp->b_vp->v_numoutput);
83 		}
84 		sp->sw_vp->v_numoutput++;
85 	}
86 	brelvp(bp);
87 	bp->b_vp = sp->sw_vp;
88 	VOP_STRATEGY(bp);
89 }
90 
91 /*
92  * System call swapon(name) enables swapping on device name,
93  * which must be in the swdevsw.  Return EBUSY
94  * if already swapping on this device.
95  */
96 swapon()
97 {
98 	struct a {
99 		char	*name;
100 	} *uap = (struct a *)u.u_ap;
101 	register struct vnode *vp;
102 	dev_t dev;
103 	register struct swdevt *sp;
104 	register struct nameidata *ndp = &u.u_nd;
105 
106 	if (u.u_error = suser(u.u_cred, &u.u_acflag))
107 		return;
108 	ndp->ni_nameiop = LOOKUP | FOLLOW;
109 	ndp->ni_segflg = UIO_USERSPACE;
110 	ndp->ni_dirp = uap->name;
111 	if (u.u_error = namei(ndp))
112 		return;
113 	vp = ndp->ni_vp;
114 	if (vp->v_type != VBLK) {
115 		vrele(vp);
116 		u.u_error = ENOTBLK;
117 		return;
118 	}
119 	dev = (dev_t)vp->v_rdev;
120 	if (major(dev) >= nblkdev) {
121 		vrele(vp);
122 		u.u_error = ENXIO;
123 		return;
124 	}
125 	for (sp = &swdevt[0]; sp->sw_dev; sp++)
126 		if (sp->sw_dev == dev) {
127 			if (sp->sw_freed) {
128 				vrele(vp);
129 				u.u_error = EBUSY;
130 				return;
131 			}
132 			sp->sw_vp = vp;
133 			if (u.u_error = swfree(sp - swdevt)) {
134 				vrele(vp);
135 				return;
136 			}
137 			return;
138 		}
139 	vrele(vp);
140 	u.u_error = EINVAL;
141 }
142 
143 /*
144  * Swfree(index) frees the index'th portion of the swap map.
145  * Each of the nswdev devices provides 1/nswdev'th of the swap
146  * space, which is laid out with blocks of dmmax pages circularly
147  * among the devices.
148  */
149 swfree(index)
150 	int index;
151 {
152 	register struct swdevt *sp;
153 	register swblk_t vsbase;
154 	register long blk;
155 	struct vnode *vp;
156 	register swblk_t dvbase;
157 	register int nblks;
158 	int error;
159 
160 	sp = &swdevt[index];
161 	vp = sp->sw_vp;
162 	if (error = VOP_OPEN(vp, FREAD|FWRITE, u.u_cred))
163 		return (error);
164 	sp->sw_freed = 1;
165 	nblks = sp->sw_nblks;
166 	for (dvbase = 0; dvbase < nblks; dvbase += dmmax) {
167 		blk = nblks - dvbase;
168 		if ((vsbase = index*dmmax + dvbase*nswdev) >= nswap)
169 			panic("swfree");
170 		if (blk > dmmax)
171 			blk = dmmax;
172 		if (vsbase == 0) {
173 			/*
174 			 * Can't free a block starting at 0 in the swapmap
175 			 * but need some space for argmap so use 1/2 this
176 			 * hunk which needs special treatment anyways.
177 			 */
178 			argdev = sp->sw_dev;
179 			if (argdev_vp)
180 				vrele(argdev_vp);
181 			VREF(vp);
182 			argdev_vp = vp;
183 			rminit(argmap, (long)(blk/2-ctod(CLSIZE)),
184 			    (long)ctod(CLSIZE), "argmap", ARGMAPSIZE);
185 			/*
186 			 * First of all chunks... initialize the swapmap
187 			 * the second half of the hunk.
188 			 */
189 			rminit(swapmap, (long)(blk/2), (long)(blk/2),
190 			    "swap", nswapmap);
191 		} else if (dvbase == 0) {
192 			/*
193 			 * Don't use the first cluster of the device
194 			 * in case it starts with a label or boot block.
195 			 */
196 			rmfree(swapmap, blk - ctod(CLSIZE),
197 			    vsbase + ctod(CLSIZE));
198 		} else
199 			rmfree(swapmap, blk, vsbase);
200 	}
201 	return (0);
202 }
203