xref: /original-bsd/sys/vm/vm_swap.c (revision cb36a3b0)
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.28 (Berkeley) 04/19/93
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 	 * If using NFS for swap, swdevt[0] will already be bdevvp'd.	XXX
51 	 */
52 	nswdev = 0;
53 	nswap = 0;
54 	for (swp = swdevt; swp->sw_dev != NODEV || swp->sw_vp != NULL; swp++) {
55 		nswdev++;
56 		if (swp->sw_nblks > nswap)
57 			nswap = swp->sw_nblks;
58 	}
59 	if (nswdev == 0)
60 		panic("swapinit");
61 	if (nswdev > 1)
62 		nswap = ((nswap + dmmax - 1) / dmmax) * dmmax;
63 	nswap *= nswdev;
64 	if (swdevt[0].sw_vp == NULL &&
65 	    bdevvp(swdevt[0].sw_dev, &swdevt[0].sw_vp))
66 		panic("swapvp");
67 	if (error = swfree(p, 0)) {
68 		printf("swfree errno %d\n", error);	/* XXX */
69 		panic("swapinit swfree 0");
70 	}
71 
72 	/*
73 	 * Now set up swap buffer headers.
74 	 */
75 	bswlist.b_actf = sp;
76 	for (i = 0; i < nswbuf - 1; i++, sp++) {
77 		sp->b_actf = sp + 1;
78 		sp->b_rcred = sp->b_wcred = p->p_ucred;
79 		sp->b_vnbufs.qe_next = NOLIST;
80 	}
81 	sp->b_rcred = sp->b_wcred = p->p_ucred;
82 	sp->b_vnbufs.qe_next = NOLIST;
83 	sp->b_actf = NULL;
84 }
85 
86 void
87 swstrategy(bp)
88 	register struct buf *bp;
89 {
90 	int sz, off, seg, index;
91 	register struct swdevt *sp;
92 	struct vnode *vp;
93 
94 #ifdef GENERIC
95 	/*
96 	 * A mini-root gets copied into the front of the swap
97 	 * and we run over top of the swap area just long
98 	 * enough for us to do a mkfs and restor of the real
99 	 * root (sure beats rewriting standalone restor).
100 	 */
101 #define	MINIROOTSIZE	4096
102 	if (rootdev == dumpdev)
103 		bp->b_blkno += MINIROOTSIZE;
104 #endif
105 	sz = howmany(bp->b_bcount, DEV_BSIZE);
106 	if (bp->b_blkno + sz > nswap) {
107 		bp->b_flags |= B_ERROR;
108 		biodone(bp);
109 		return;
110 	}
111 	if (nswdev > 1) {
112 		off = bp->b_blkno % dmmax;
113 		if (off+sz > dmmax) {
114 			bp->b_flags |= B_ERROR;
115 			biodone(bp);
116 			return;
117 		}
118 		seg = bp->b_blkno / dmmax;
119 		index = seg % nswdev;
120 		seg /= nswdev;
121 		bp->b_blkno = seg*dmmax + off;
122 	} else
123 		index = 0;
124 	sp = &swdevt[index];
125 	if ((bp->b_dev = sp->sw_dev) == NODEV)
126 		panic("swstrategy");
127 	if (sp->sw_vp == NULL) {
128 		bp->b_error |= B_ERROR;
129 		biodone(bp);
130 		return;
131 	}
132 	VHOLD(sp->sw_vp);
133 	if ((bp->b_flags & B_READ) == 0) {
134 		if (vp = bp->b_vp) {
135 			vp->v_numoutput--;
136 			if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
137 				vp->v_flag &= ~VBWAIT;
138 				wakeup((caddr_t)&vp->v_numoutput);
139 			}
140 		}
141 		sp->sw_vp->v_numoutput++;
142 	}
143 	if (bp->b_vp != NULL)
144 		brelvp(bp);
145 	bp->b_vp = sp->sw_vp;
146 	VOP_STRATEGY(bp);
147 }
148 
149 /*
150  * System call swapon(name) enables swapping on device name,
151  * which must be in the swdevsw.  Return EBUSY
152  * if already swapping on this device.
153  */
154 struct swapon_args {
155 	char	*name;
156 };
157 /* ARGSUSED */
158 int
159 swapon(p, uap, retval)
160 	struct proc *p;
161 	struct swapon_args *uap;
162 	int *retval;
163 {
164 	register struct vnode *vp;
165 	register struct swdevt *sp;
166 	dev_t dev;
167 	int error;
168 	struct nameidata nd;
169 
170 	if (error = suser(p->p_ucred, &p->p_acflag))
171 		return (error);
172 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->name, p);
173 	if (error = namei(&nd))
174 		return (error);
175 	vp = nd.ni_vp;
176 	if (vp->v_type != VBLK) {
177 		vrele(vp);
178 		return (ENOTBLK);
179 	}
180 	dev = (dev_t)vp->v_rdev;
181 	if (major(dev) >= nblkdev) {
182 		vrele(vp);
183 		return (ENXIO);
184 	}
185 	for (sp = &swdevt[0]; sp->sw_dev != NODEV; sp++)
186 		if (sp->sw_dev == dev) {
187 			if (sp->sw_freed) {
188 				vrele(vp);
189 				return (EBUSY);
190 			}
191 			sp->sw_vp = vp;
192 			if (error = swfree(p, sp - swdevt)) {
193 				vrele(vp);
194 				return (error);
195 			}
196 			return (0);
197 		}
198 	vrele(vp);
199 	return (EINVAL);
200 }
201 
202 /*
203  * Swfree(index) frees the index'th portion of the swap map.
204  * Each of the nswdev devices provides 1/nswdev'th of the swap
205  * space, which is laid out with blocks of dmmax pages circularly
206  * among the devices.
207  */
208 int
209 swfree(p, index)
210 	struct proc *p;
211 	int index;
212 {
213 	register struct swdevt *sp;
214 	register swblk_t vsbase;
215 	register long blk;
216 	struct vnode *vp;
217 	register swblk_t dvbase;
218 	register int nblks;
219 	int error;
220 
221 	sp = &swdevt[index];
222 	vp = sp->sw_vp;
223 	if (error = VOP_OPEN(vp, FREAD|FWRITE, p->p_ucred, p))
224 		return (error);
225 	sp->sw_freed = 1;
226 	nblks = sp->sw_nblks;
227 	for (dvbase = 0; dvbase < nblks; dvbase += dmmax) {
228 		blk = nblks - dvbase;
229 		if ((vsbase = index*dmmax + dvbase*nswdev) >= nswap)
230 			panic("swfree");
231 		if (blk > dmmax)
232 			blk = dmmax;
233 		if (vsbase == 0) {
234 			/*
235 			 * First of all chunks... initialize the swapmap
236 			 * the second half of the hunk.
237 			 */
238 			rminit(swapmap, (long)(blk/2), (long)(blk/2),
239 			    "swap", nswapmap);
240 		} else if (dvbase == 0) {
241 			/*
242 			 * Don't use the first cluster of the device
243 			 * in case it starts with a label or boot block.
244 			 */
245 			rmfree(swapmap, blk - ctod(CLSIZE),
246 			    vsbase + ctod(CLSIZE));
247 		} else
248 			rmfree(swapmap, blk, vsbase);
249 	}
250 	return (0);
251 }
252