xref: /original-bsd/sys/vm/vm_swap.c (revision 72b6fd44)
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.12 (Berkeley) 06/21/90
18  */
19 
20 #include "param.h"
21 #include "systm.h"
22 #include "buf.h"
23 #include "conf.h"
24 #include "syscontext.h"
25 #include "vnode.h"
26 #include "specdev.h"
27 #include "map.h"
28 #include "file.h"
29 
30 /*
31  * Indirect driver for multi-controller paging.
32  */
33 swstrategy(bp)
34 	register struct buf *bp;
35 {
36 	int sz, off, seg, index;
37 	register struct swdevt *sp;
38 	struct vnode *vp;
39 
40 #ifdef GENERIC
41 	/*
42 	 * A mini-root gets copied into the front of the swap
43 	 * and we run over top of the swap area just long
44 	 * enough for us to do a mkfs and restor of the real
45 	 * root (sure beats rewriting standalone restor).
46 	 */
47 #define	MINIROOTSIZE	4096
48 	if (rootdev == dumpdev)
49 		bp->b_blkno += MINIROOTSIZE;
50 #endif
51 	sz = howmany(bp->b_bcount, DEV_BSIZE);
52 	if (bp->b_blkno+sz > nswap) {
53 		bp->b_flags |= B_ERROR;
54 		biodone(bp);
55 		return;
56 	}
57 	if (nswdev > 1) {
58 		off = bp->b_blkno % dmmax;
59 		if (off+sz > dmmax) {
60 			bp->b_flags |= B_ERROR;
61 			biodone(bp);
62 			return;
63 		}
64 		seg = bp->b_blkno / dmmax;
65 		index = seg % nswdev;
66 		seg /= nswdev;
67 		bp->b_blkno = seg*dmmax + off;
68 	} else
69 		index = 0;
70 	sp = &swdevt[index];
71 	if ((bp->b_dev = sp->sw_dev) == 0)
72 		panic("swstrategy");
73 	if (sp->sw_vp == NULL) {
74 		bp->b_error |= B_ERROR;
75 		biodone(bp);
76 		return;
77 	}
78 	VHOLD(sp->sw_vp);
79 	if ((bp->b_flags & B_READ) == 0) {
80 		if (vp = bp->b_vp) {
81 			vp->v_numoutput--;
82 			if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
83 				vp->v_flag &= ~VBWAIT;
84 				wakeup((caddr_t)&vp->v_numoutput);
85 			}
86 		}
87 		sp->sw_vp->v_numoutput++;
88 	}
89 	if (bp->b_vp != NULL)
90 		brelvp(bp);
91 	bp->b_vp = sp->sw_vp;
92 	VOP_STRATEGY(bp);
93 }
94 
95 /*
96  * System call swapon(name) enables swapping on device name,
97  * which must be in the swdevsw.  Return EBUSY
98  * if already swapping on this device.
99  */
100 /* ARGSUSED */
101 swapon(p, uap, retval)
102 	struct proc *p;
103 	struct args {
104 		char	*name;
105 	} *uap;
106 	int *retval;
107 {
108 	register struct vnode *vp;
109 	register struct swdevt *sp;
110 	register struct nameidata *ndp = &u.u_nd;
111 	dev_t dev;
112 	int error;
113 
114 	if (error = suser(u.u_cred, &u.u_acflag))
115 		RETURN (error);
116 	ndp->ni_nameiop = LOOKUP | FOLLOW;
117 	ndp->ni_segflg = UIO_USERSPACE;
118 	ndp->ni_dirp = uap->name;
119 	if (error = namei(ndp))
120 		RETURN (error);
121 	vp = ndp->ni_vp;
122 	if (vp->v_type != VBLK) {
123 		vrele(vp);
124 		RETURN (ENOTBLK);
125 	}
126 	dev = (dev_t)vp->v_rdev;
127 	if (major(dev) >= nblkdev) {
128 		vrele(vp);
129 		RETURN (ENXIO);
130 	}
131 	for (sp = &swdevt[0]; sp->sw_dev; sp++)
132 		if (sp->sw_dev == dev) {
133 			if (sp->sw_freed) {
134 				vrele(vp);
135 				RETURN (EBUSY);
136 			}
137 			sp->sw_vp = vp;
138 			if (error = swfree(sp - swdevt)) {
139 				vrele(vp);
140 				RETURN (error);
141 			}
142 			RETURN (0);
143 		}
144 	vrele(vp);
145 	RETURN (EINVAL);
146 }
147 
148 /*
149  * Swfree(index) frees the index'th portion of the swap map.
150  * Each of the nswdev devices provides 1/nswdev'th of the swap
151  * space, which is laid out with blocks of dmmax pages circularly
152  * among the devices.
153  */
154 swfree(index)
155 	int index;
156 {
157 	register struct swdevt *sp;
158 	register swblk_t vsbase;
159 	register long blk;
160 	struct vnode *vp;
161 	register swblk_t dvbase;
162 	register int nblks;
163 	int error;
164 
165 	sp = &swdevt[index];
166 	vp = sp->sw_vp;
167 	if (error = VOP_OPEN(vp, FREAD|FWRITE, u.u_cred))
168 		return (error);
169 	sp->sw_freed = 1;
170 	nblks = sp->sw_nblks;
171 	for (dvbase = 0; dvbase < nblks; dvbase += dmmax) {
172 		blk = nblks - dvbase;
173 		if ((vsbase = index*dmmax + dvbase*nswdev) >= nswap)
174 			panic("swfree");
175 		if (blk > dmmax)
176 			blk = dmmax;
177 		if (vsbase == 0) {
178 			/*
179 			 * Can't free a block starting at 0 in the swapmap
180 			 * but need some space for argmap so use 1/2 this
181 			 * hunk which needs special treatment anyways.
182 			 */
183 			argdev = sp->sw_dev;
184 			if (argdev_vp)
185 				vrele(argdev_vp);
186 			VREF(vp);
187 			argdev_vp = vp;
188 			rminit(argmap, (long)(blk/2-ctod(CLSIZE)),
189 			    (long)ctod(CLSIZE), "argmap", ARGMAPSIZE);
190 			/*
191 			 * First of all chunks... initialize the swapmap
192 			 * the second half of the hunk.
193 			 */
194 			rminit(swapmap, (long)(blk/2), (long)(blk/2),
195 			    "swap", nswapmap);
196 		} else if (dvbase == 0) {
197 			/*
198 			 * Don't use the first cluster of the device
199 			 * in case it starts with a label or boot block.
200 			 */
201 			rmfree(swapmap, blk - ctod(CLSIZE),
202 			    vsbase + ctod(CLSIZE));
203 		} else
204 			rmfree(swapmap, blk, vsbase);
205 	}
206 	return (0);
207 }
208