xref: /original-bsd/sys/vm/vm_swap.c (revision 860e07fc)
1 /*
2  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)vm_swap.c	7.25 (Berkeley) 07/12/92
8  */
9 
10 #include <sys/param.h>
11 #include <sys/systm.h>
12 #include <sys/buf.h>
13 #include <sys/conf.h>
14 #include <sys/proc.h>
15 #include <sys/namei.h>
16 #include <sys/dmap.h>		/* XXX */
17 #include <sys/vnode.h>
18 #include <sys/map.h>
19 #include <sys/file.h>
20 
21 #include <miscfs/specfs/specdev.h>
22 
23 /*
24  * Indirect driver for multi-controller paging.
25  */
26 
27 int	nswap, nswdev;
28 
29 /*
30  * Set up swap devices.
31  * Initialize linked list of free swap
32  * headers. These do not actually point
33  * to buffers, but rather to pages that
34  * are being swapped in and out.
35  */
36 void
37 swapinit()
38 {
39 	register int i;
40 	register struct buf *sp = swbuf;
41 	register struct proc *p = &proc0;	/* XXX */
42 	struct swdevt *swp;
43 	int error;
44 
45 	/*
46 	 * Count swap devices, and adjust total swap space available.
47 	 * Some of this space will not be available until a swapon()
48 	 * system is issued, usually when the system goes multi-user.
49 	 */
50 	nswdev = 0;
51 	nswap = 0;
52 	for (swp = swdevt; swp->sw_dev; swp++) {
53 		nswdev++;
54 		if (swp->sw_nblks > nswap)
55 			nswap = swp->sw_nblks;
56 	}
57 	if (nswdev == 0)
58 		panic("swapinit");
59 	if (nswdev > 1)
60 		nswap = ((nswap + dmmax - 1) / dmmax) * dmmax;
61 	nswap *= nswdev;
62 	if (bdevvp(swdevt[0].sw_dev, &swdevt[0].sw_vp))
63 		panic("swapvp");
64 	if (error = swfree(p, 0)) {
65 		printf("swfree errno %d\n", error);	/* XXX */
66 		panic("swapinit swfree 0");
67 	}
68 
69 	/*
70 	 * Now set up swap buffer headers.
71 	 */
72 	bswlist.av_forw = sp;
73 	for (i = 0; i < nswbuf - 1; i++, sp++) {
74 		sp->av_forw = sp + 1;
75 		sp->b_rcred = sp->b_wcred = p->p_ucred;
76 	}
77 	sp->b_rcred = sp->b_wcred = p->p_ucred;
78 	sp->av_forw = NULL;
79 }
80 
81 void
82 swstrategy(bp)
83 	register struct buf *bp;
84 {
85 	int sz, off, seg, index;
86 	register struct swdevt *sp;
87 	struct vnode *vp;
88 
89 #ifdef GENERIC
90 	/*
91 	 * A mini-root gets copied into the front of the swap
92 	 * and we run over top of the swap area just long
93 	 * enough for us to do a mkfs and restor of the real
94 	 * root (sure beats rewriting standalone restor).
95 	 */
96 #define	MINIROOTSIZE	4096
97 	if (rootdev == dumpdev)
98 		bp->b_blkno += MINIROOTSIZE;
99 #endif
100 	sz = howmany(bp->b_bcount, DEV_BSIZE);
101 	if (bp->b_blkno + sz > nswap) {
102 		bp->b_flags |= B_ERROR;
103 		biodone(bp);
104 		return;
105 	}
106 	if (nswdev > 1) {
107 		off = bp->b_blkno % dmmax;
108 		if (off+sz > dmmax) {
109 			bp->b_flags |= B_ERROR;
110 			biodone(bp);
111 			return;
112 		}
113 		seg = bp->b_blkno / dmmax;
114 		index = seg % nswdev;
115 		seg /= nswdev;
116 		bp->b_blkno = seg*dmmax + off;
117 	} else
118 		index = 0;
119 	sp = &swdevt[index];
120 	if ((bp->b_dev = sp->sw_dev) == 0)
121 		panic("swstrategy");
122 	if (sp->sw_vp == NULL) {
123 		bp->b_error |= B_ERROR;
124 		biodone(bp);
125 		return;
126 	}
127 	VHOLD(sp->sw_vp);
128 	if ((bp->b_flags & B_READ) == 0) {
129 		if (vp = bp->b_vp) {
130 			vp->v_numoutput--;
131 			if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
132 				vp->v_flag &= ~VBWAIT;
133 				wakeup((caddr_t)&vp->v_numoutput);
134 			}
135 		}
136 		sp->sw_vp->v_numoutput++;
137 	}
138 	if (bp->b_vp != NULL)
139 		brelvp(bp);
140 	bp->b_vp = sp->sw_vp;
141 	VOP_STRATEGY(bp);
142 }
143 
144 /*
145  * System call swapon(name) enables swapping on device name,
146  * which must be in the swdevsw.  Return EBUSY
147  * if already swapping on this device.
148  */
149 struct swapon_args {
150 	char	*name;
151 };
152 /* ARGSUSED */
153 int
154 swapon(p, uap, retval)
155 	struct proc *p;
156 	struct swapon_args *uap;
157 	int *retval;
158 {
159 	register struct vnode *vp;
160 	register struct swdevt *sp;
161 	dev_t dev;
162 	int error;
163 	struct nameidata nd;
164 
165 	if (error = suser(p->p_ucred, &p->p_acflag))
166 		return (error);
167 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->name, p);
168 	if (error = namei(&nd))
169 		return (error);
170 	vp = nd.ni_vp;
171 	if (vp->v_type != VBLK) {
172 		vrele(vp);
173 		return (ENOTBLK);
174 	}
175 	dev = (dev_t)vp->v_rdev;
176 	if (major(dev) >= nblkdev) {
177 		vrele(vp);
178 		return (ENXIO);
179 	}
180 	for (sp = &swdevt[0]; sp->sw_dev; sp++)
181 		if (sp->sw_dev == dev) {
182 			if (sp->sw_freed) {
183 				vrele(vp);
184 				return (EBUSY);
185 			}
186 			sp->sw_vp = vp;
187 			if (error = swfree(p, sp - swdevt)) {
188 				vrele(vp);
189 				return (error);
190 			}
191 			return (0);
192 		}
193 	vrele(vp);
194 	return (EINVAL);
195 }
196 
197 /*
198  * Swfree(index) frees the index'th portion of the swap map.
199  * Each of the nswdev devices provides 1/nswdev'th of the swap
200  * space, which is laid out with blocks of dmmax pages circularly
201  * among the devices.
202  */
203 int
204 swfree(p, index)
205 	struct proc *p;
206 	int index;
207 {
208 	register struct swdevt *sp;
209 	register swblk_t vsbase;
210 	register long blk;
211 	struct vnode *vp;
212 	register swblk_t dvbase;
213 	register int nblks;
214 	int error;
215 
216 	sp = &swdevt[index];
217 	vp = sp->sw_vp;
218 	if (error = VOP_OPEN(vp, FREAD|FWRITE, p->p_ucred, p))
219 		return (error);
220 	sp->sw_freed = 1;
221 	nblks = sp->sw_nblks;
222 	for (dvbase = 0; dvbase < nblks; dvbase += dmmax) {
223 		blk = nblks - dvbase;
224 		if ((vsbase = index*dmmax + dvbase*nswdev) >= nswap)
225 			panic("swfree");
226 		if (blk > dmmax)
227 			blk = dmmax;
228 		if (vsbase == 0) {
229 			/*
230 			 * First of all chunks... initialize the swapmap
231 			 * the second half of the hunk.
232 			 */
233 			rminit(swapmap, (long)(blk/2), (long)(blk/2),
234 			    "swap", nswapmap);
235 		} else if (dvbase == 0) {
236 			/*
237 			 * Don't use the first cluster of the device
238 			 * in case it starts with a label or boot block.
239 			 */
240 			rmfree(swapmap, blk - ctod(CLSIZE),
241 			    vsbase + ctod(CLSIZE));
242 		} else
243 			rmfree(swapmap, blk, vsbase);
244 	}
245 	return (0);
246 }
247