xref: /original-bsd/sys/ufs/ufs/ufs_disksubr.c (revision 7b081c7c)
1 /*
2  * Copyright (c) 1982, 1986, 1988 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  *	@(#)ufs_disksubr.c	7.13 (Berkeley) 01/02/90
18  */
19 
20 #include "param.h"
21 #include "systm.h"
22 #include "buf.h"
23 #include "disklabel.h"
24 #include "syslog.h"
25 #include "user.h"
26 
27 /*
28  * Seek sort for disks.  We depend on the driver
29  * which calls us using b_resid as the current cylinder number.
30  *
31  * The argument dp structure holds a b_actf activity chain pointer
32  * on which we keep two queues, sorted in ascending cylinder order.
33  * The first queue holds those requests which are positioned after
34  * the current cylinder (in the first request); the second holds
35  * requests which came in after their cylinder number was passed.
36  * Thus we implement a one way scan, retracting after reaching the
37  * end of the drive to the first request on the second queue,
38  * at which time it becomes the first queue.
39  *
40  * A one-way scan is natural because of the way UNIX read-ahead
41  * blocks are allocated.
42  */
43 
44 #define	b_cylin	b_resid
45 
46 disksort(dp, bp)
47 	register struct buf *dp, *bp;
48 {
49 	register struct buf *ap;
50 
51 	/*
52 	 * If nothing on the activity queue, then
53 	 * we become the only thing.
54 	 */
55 	ap = dp->b_actf;
56 	if(ap == NULL) {
57 		dp->b_actf = bp;
58 		dp->b_actl = bp;
59 		bp->av_forw = NULL;
60 		return;
61 	}
62 	/*
63 	 * If we lie after the first (currently active)
64 	 * request, then we must locate the second request list
65 	 * and add ourselves to it.
66 	 */
67 	if (bp->b_cylin < ap->b_cylin) {
68 		while (ap->av_forw) {
69 			/*
70 			 * Check for an ``inversion'' in the
71 			 * normally ascending cylinder numbers,
72 			 * indicating the start of the second request list.
73 			 */
74 			if (ap->av_forw->b_cylin < ap->b_cylin) {
75 				/*
76 				 * Search the second request list
77 				 * for the first request at a larger
78 				 * cylinder number.  We go before that;
79 				 * if there is no such request, we go at end.
80 				 */
81 				do {
82 					if (bp->b_cylin < ap->av_forw->b_cylin)
83 						goto insert;
84 					if (bp->b_cylin == ap->av_forw->b_cylin &&
85 					    bp->b_blkno < ap->av_forw->b_blkno)
86 						goto insert;
87 					ap = ap->av_forw;
88 				} while (ap->av_forw);
89 				goto insert;		/* after last */
90 			}
91 			ap = ap->av_forw;
92 		}
93 		/*
94 		 * No inversions... we will go after the last, and
95 		 * be the first request in the second request list.
96 		 */
97 		goto insert;
98 	}
99 	/*
100 	 * Request is at/after the current request...
101 	 * sort in the first request list.
102 	 */
103 	while (ap->av_forw) {
104 		/*
105 		 * We want to go after the current request
106 		 * if there is an inversion after it (i.e. it is
107 		 * the end of the first request list), or if
108 		 * the next request is a larger cylinder than our request.
109 		 */
110 		if (ap->av_forw->b_cylin < ap->b_cylin ||
111 		    bp->b_cylin < ap->av_forw->b_cylin ||
112 		    (bp->b_cylin == ap->av_forw->b_cylin &&
113 		    bp->b_blkno < ap->av_forw->b_blkno))
114 			goto insert;
115 		ap = ap->av_forw;
116 	}
117 	/*
118 	 * Neither a second list nor a larger
119 	 * request... we go at the end of the first list,
120 	 * which is the same as the end of the whole schebang.
121 	 */
122 insert:
123 	bp->av_forw = ap->av_forw;
124 	ap->av_forw = bp;
125 	if (ap == dp->b_actl)
126 		dp->b_actl = bp;
127 }
128 
129 /*
130  * Attempt to read a disk label from a device
131  * using the indicated stategy routine.
132  * The label must be partly set up before this:
133  * secpercyl and anything required in the strategy routine
134  * (e.g., sector size) must be filled in before calling us.
135  * Returns null on success and an error string on failure.
136  */
137 char *
138 readdisklabel(dev, strat, lp)
139 	dev_t dev;
140 	int (*strat)();
141 	register struct disklabel *lp;
142 {
143 	register struct buf *bp;
144 	struct disklabel *dlp;
145 	char *msg = NULL;
146 
147 	if (lp->d_secperunit == 0)
148 		lp->d_secperunit = 0x1fffffff;
149 	lp->d_npartitions = 1;
150 	if (lp->d_partitions[0].p_size == 0)
151 		lp->d_partitions[0].p_size = 0x1fffffff;
152 	lp->d_partitions[0].p_offset = 0;
153 
154 	bp = geteblk((int)lp->d_secsize);
155 	bp->b_dev = dev;
156 	bp->b_blkno = LABELSECTOR;
157 	bp->b_bcount = lp->d_secsize;
158 	bp->b_flags = B_BUSY | B_READ;
159 	bp->b_cylin = LABELSECTOR / lp->d_secpercyl;
160 	(*strat)(bp);
161 	if (biowait(bp)) {
162 		msg = "I/O error";
163 	} else for (dlp = (struct disklabel *)bp->b_un.b_addr;
164 	    dlp <= (struct disklabel *)(bp->b_un.b_addr+DEV_BSIZE-sizeof(*dlp));
165 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
166 		if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
167 			if (msg == NULL)
168 				msg = "no disk label";
169 		} else if (dlp->d_npartitions > MAXPARTITIONS ||
170 			   dkcksum(dlp) != 0)
171 			msg = "disk label corrupted";
172 		else {
173 			*lp = *dlp;
174 			msg = NULL;
175 			break;
176 		}
177 	}
178 	bp->b_flags = B_INVAL | B_AGE;
179 	brelse(bp);
180 	return (msg);
181 }
182 
183 /*
184  * Check new disk label for sensibility
185  * before setting it.
186  */
187 setdisklabel(olp, nlp, openmask)
188 	register struct disklabel *olp, *nlp;
189 	u_long openmask;
190 {
191 	register i;
192 	register struct partition *opp, *npp;
193 
194 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
195 	    dkcksum(nlp) != 0)
196 		return (EINVAL);
197 	while ((i = ffs((long)openmask)) != 0) {
198 		i--;
199 		openmask &= ~(1 << i);
200 		if (nlp->d_npartitions <= i)
201 			return (EBUSY);
202 		opp = &olp->d_partitions[i];
203 		npp = &nlp->d_partitions[i];
204 		if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
205 			return (EBUSY);
206 		/*
207 		 * Copy internally-set partition information
208 		 * if new label doesn't include it.		XXX
209 		 */
210 		if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
211 			npp->p_fstype = opp->p_fstype;
212 			npp->p_fsize = opp->p_fsize;
213 			npp->p_frag = opp->p_frag;
214 			npp->p_cpg = opp->p_cpg;
215 		}
216 	}
217  	nlp->d_checksum = 0;
218  	nlp->d_checksum = dkcksum(nlp);
219 	*olp = *nlp;
220 	return (0);
221 }
222 
223 /* encoding of disk minor numbers, should be elsewhere... */
224 #define dkunit(dev)		(minor(dev) >> 3)
225 #define dkpart(dev)		(minor(dev) & 07)
226 #define dkminor(unit, part)	(((unit) << 3) | (part))
227 
228 /*
229  * Write disk label back to device after modification.
230  */
231 writedisklabel(dev, strat, lp)
232 	dev_t dev;
233 	int (*strat)();
234 	register struct disklabel *lp;
235 {
236 	struct buf *bp;
237 	struct disklabel *dlp;
238 	int labelpart;
239 	int error = 0;
240 
241 	labelpart = dkpart(dev);
242 	if (lp->d_partitions[labelpart].p_offset != 0) {
243 		if (lp->d_partitions[0].p_offset != 0)
244 			return (EXDEV);			/* not quite right */
245 		labelpart = 0;
246 	}
247 	bp = geteblk((int)lp->d_secsize);
248 	bp->b_dev = makedev(major(dev), dkminor(dkunit(dev), labelpart));
249 	bp->b_blkno = LABELSECTOR;
250 	bp->b_bcount = lp->d_secsize;
251 	bp->b_flags = B_READ;
252 	(*strat)(bp);
253 	if (error = biowait(bp))
254 		goto done;
255 	for (dlp = (struct disklabel *)bp->b_un.b_addr;
256 	    dlp <= (struct disklabel *)
257 	      (bp->b_un.b_addr + lp->d_secsize - sizeof(*dlp));
258 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
259 		if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
260 		    dkcksum(dlp) == 0) {
261 			*dlp = *lp;
262 			bp->b_flags = B_WRITE;
263 			(*strat)(bp);
264 			error = biowait(bp);
265 			goto done;
266 		}
267 	}
268 	error = ESRCH;
269 done:
270 	brelse(bp);
271 	return (error);
272 }
273 
274 /*
275  * Compute checksum for disk label.
276  */
277 dkcksum(lp)
278 	register struct disklabel *lp;
279 {
280 	register u_short *start, *end;
281 	register u_short sum = 0;
282 
283 	start = (u_short *)lp;
284 	end = (u_short *)&lp->d_partitions[lp->d_npartitions];
285 	while (start < end)
286 		sum ^= *start++;
287 	return (sum);
288 }
289 
290 /*
291  * Disk error is the preface to plaintive error messages
292  * about failing disk transfers.  It prints messages of the form
293 
294 hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d)
295 
296  * if the offset of the error in the transfer and a disk label
297  * are both available.  blkdone should be -1 if the position of the error
298  * is unknown; the disklabel pointer may be null from drivers that have not
299  * been converted to use them.  The message is printed with printf
300  * if pri is LOG_PRINTF, otherwise it uses log at the specified priority.
301  * The message should be completed (with at least a newline) with printf
302  * or addlog, respectively.  There is no trailing space.
303  */
304 diskerr(bp, dname, what, pri, blkdone, lp)
305 	register struct buf *bp;
306 	char *dname, *what;
307 	int pri, blkdone;
308 	register struct disklabel *lp;
309 {
310 	int unit = dkunit(bp->b_dev), part = dkpart(bp->b_dev);
311 	register int (*pr)(), sn;
312 	char partname = 'a' + part;
313 	extern printf(), addlog();
314 
315 	if (pri != LOG_PRINTF) {
316 		log(pri, "");
317 		pr = addlog;
318 	} else
319 		pr = printf;
320 	(*pr)("%s%d%c: %s %sing fsbn ", dname, unit, partname, what,
321 	    bp->b_flags & B_READ ? "read" : "writ");
322 	sn = bp->b_blkno;
323 	if (bp->b_bcount <= DEV_BSIZE)
324 		(*pr)("%d", sn);
325 	else {
326 		if (blkdone >= 0) {
327 			sn += blkdone;
328 			(*pr)("%d of ", sn);
329 		}
330 		(*pr)("%d-%d", bp->b_blkno,
331 		    bp->b_blkno + (bp->b_bcount - 1) / DEV_BSIZE);
332 	}
333 	if (lp && (blkdone >= 0 || bp->b_bcount <= lp->d_secsize)) {
334 #ifdef tahoe
335 		sn *= DEV_BSIZE / lp->d_secsize;		/* XXX */
336 #endif
337 		sn += lp->d_partitions[part].p_offset;
338 		(*pr)(" (%s%d bn %d; cn %d", dname, unit, sn,
339 		    sn / lp->d_secpercyl);
340 		sn %= lp->d_secpercyl;
341 		(*pr)(" tn %d sn %d)", sn / lp->d_nsectors, sn % lp->d_nsectors);
342 	}
343 }
344