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